Fix WinBoard Lance bug
[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, 2010 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((char *s));\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 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 Boolean saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 int errorExitStatus;\r
159 \r
160 BoardSize boardSize;\r
161 Boolean chessProgram;\r
162 //static int boardX, boardY;\r
163 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
164 int squareSize, lineGap, minorSize;\r
165 static int winW, winH;\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
167 static int logoHeight = 0;\r
168 static char messageText[MESSAGE_TEXT_MAX];\r
169 static int clockTimerEvent = 0;\r
170 static int loadGameTimerEvent = 0;\r
171 static int analysisTimerEvent = 0;\r
172 static DelayedEventCallback delayedTimerCallback;\r
173 static int delayedTimerEvent = 0;\r
174 static int buttonCount = 2;\r
175 char *icsTextMenuString;\r
176 char *icsNames;\r
177 char *firstChessProgramNames;\r
178 char *secondChessProgramNames;\r
179 \r
180 #define PALETTESIZE 256\r
181 \r
182 HINSTANCE hInst;          /* current instance */\r
183 Boolean alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][8];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407 if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
408     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
409     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
410         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
411         if(strlen(buf) == 0) continue;\r
412         s = T_(buf);\r
413         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
414     }\r
415 }\r
416 \r
417 void\r
418 TranslateMenus(int addLanguage)\r
419 {\r
420     int i, j;\r
421     WIN32_FIND_DATA fileData;\r
422     HANDLE hFind;\r
423 #define IDM_English 1895\r
424     if(1) {\r
425         HMENU mainMenu = GetMenu(hwndMain);\r
426         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
427           HMENU subMenu = GetSubMenu(mainMenu, i);\r
428           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
429                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
430           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
431             char buf[MSG_SIZ];\r
432             UINT k = GetMenuItemID(subMenu, j);\r
433               if(menuText[i][j])
434                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
435                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
436                 menuText[i][j] = strdup(buf); // remember original on first change\r
437             }\r
438             if(buf[0] == NULLCHAR) continue;\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
440             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
441                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
442                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
443           }\r
444         }\r
445         DrawMenuBar(hwndMain);\r
446     }\r
447 \r
448     if(!addLanguage) return;\r
449     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
452         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
453         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
454         i = 0; lastChecked = IDM_English;\r
455         do {\r
456             char *p, *q = fileData.cFileName;\r
457             int checkFlag = MF_UNCHECKED;\r
458             languageFile[i] = strdup(q);\r
459             if(barbaric && !strcmp(oldLanguage, q)) {\r
460                 checkFlag = MF_CHECKED;\r
461                 lastChecked = IDM_English + i + 1;\r
462                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
463             }\r
464             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
465             p = strstr(fileData.cFileName, ".lng");\r
466             if(p) *p = 0;\r
467             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
468         } while(FindNextFile(hFind, &fileData));\r
469         FindClose(hFind);\r
470     }\r
471 }\r
472 \r
473 #endif\r
474 \r
475 typedef struct {\r
476   char *name;\r
477   int squareSize;\r
478   int lineGap;\r
479   int smallLayout;\r
480   int tinyLayout;\r
481   int cliWidth, cliHeight;\r
482 } SizeInfo;\r
483 \r
484 SizeInfo sizeInfo[] = \r
485 {\r
486   { "tiny",     21, 0, 1, 1, 0, 0 },\r
487   { "teeny",    25, 1, 1, 1, 0, 0 },\r
488   { "dinky",    29, 1, 1, 1, 0, 0 },\r
489   { "petite",   33, 1, 1, 1, 0, 0 },\r
490   { "slim",     37, 2, 1, 0, 0, 0 },\r
491   { "small",    40, 2, 1, 0, 0, 0 },\r
492   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
493   { "middling", 49, 2, 0, 0, 0, 0 },\r
494   { "average",  54, 2, 0, 0, 0, 0 },\r
495   { "moderate", 58, 3, 0, 0, 0, 0 },\r
496   { "medium",   64, 3, 0, 0, 0, 0 },\r
497   { "bulky",    72, 3, 0, 0, 0, 0 },\r
498   { "large",    80, 3, 0, 0, 0, 0 },\r
499   { "big",      87, 3, 0, 0, 0, 0 },\r
500   { "huge",     95, 3, 0, 0, 0, 0 },\r
501   { "giant",    108, 3, 0, 0, 0, 0 },\r
502   { "colossal", 116, 4, 0, 0, 0, 0 },\r
503   { "titanic",  129, 4, 0, 0, 0, 0 },\r
504   { NULL, 0, 0, 0, 0, 0, 0 }\r
505 };\r
506 \r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
509 {\r
510   { 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
511   { 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
512   { 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
513   { 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
514   { 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
515   { 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
516   { 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
517   { 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
518   { 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
519   { 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
520   { 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
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528 };\r
529 \r
530 MyFont *font[NUM_SIZES][NUM_FONTS];\r
531 \r
532 typedef struct {\r
533   char *label;\r
534   int id;\r
535   HWND hwnd;\r
536   WNDPROC wndproc;\r
537 } MyButtonDesc;\r
538 \r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
540 #define N_BUTTONS 5\r
541 \r
542 MyButtonDesc buttonDesc[N_BUTTONS] =\r
543 {\r
544   {"<<", IDM_ToStart, NULL, NULL},\r
545   {"<", IDM_Backward, NULL, NULL},\r
546   {"P", IDM_Pause, NULL, NULL},\r
547   {">", IDM_Forward, NULL, NULL},\r
548   {">>", IDM_ToEnd, NULL, NULL},\r
549 };\r
550 \r
551 int tinyLayout = 0, smallLayout = 0;\r
552 #define MENU_BAR_ITEMS 7\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
556 };\r
557 \r
558 \r
559 MySound sounds[(int)NSoundClasses];\r
560 MyTextAttribs textAttribs[(int)NColorClasses];\r
561 \r
562 MyColorizeAttribs colorizeAttribs[] = {\r
563   { (COLORREF)0, 0, N_("Shout Text") },\r
564   { (COLORREF)0, 0, N_("SShout/CShout") },\r
565   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
566   { (COLORREF)0, 0, N_("Channel Text") },\r
567   { (COLORREF)0, 0, N_("Kibitz Text") },\r
568   { (COLORREF)0, 0, N_("Tell Text") },\r
569   { (COLORREF)0, 0, N_("Challenge Text") },\r
570   { (COLORREF)0, 0, N_("Request Text") },\r
571   { (COLORREF)0, 0, N_("Seek Text") },\r
572   { (COLORREF)0, 0, N_("Normal Text") },\r
573   { (COLORREF)0, 0, N_("None") }\r
574 };\r
575 \r
576 \r
577 \r
578 static char *commentTitle;\r
579 static char *commentText;\r
580 static int commentIndex;\r
581 static Boolean editComment = FALSE;\r
582 \r
583 \r
584 char errorTitle[MSG_SIZ];\r
585 char errorMessage[2*MSG_SIZ];\r
586 HWND errorDialog = NULL;\r
587 BOOLEAN moveErrorMessageUp = FALSE;\r
588 BOOLEAN consoleEcho = TRUE;\r
589 CHARFORMAT consoleCF;\r
590 COLORREF consoleBackgroundColor;\r
591 \r
592 char *programVersion;\r
593 \r
594 #define CPReal 1\r
595 #define CPComm 2\r
596 #define CPSock 3\r
597 #define CPRcmd 4\r
598 typedef int CPKind;\r
599 \r
600 typedef struct {\r
601   CPKind kind;\r
602   HANDLE hProcess;\r
603   DWORD pid;\r
604   HANDLE hTo;\r
605   HANDLE hFrom;\r
606   SOCKET sock;\r
607   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
608 } ChildProc;\r
609 \r
610 #define INPUT_SOURCE_BUF_SIZE 4096\r
611 \r
612 typedef struct _InputSource {\r
613   CPKind kind;\r
614   HANDLE hFile;\r
615   SOCKET sock;\r
616   int lineByLine;\r
617   HANDLE hThread;\r
618   DWORD id;\r
619   char buf[INPUT_SOURCE_BUF_SIZE];\r
620   char *next;\r
621   DWORD count;\r
622   int error;\r
623   InputCallback func;\r
624   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
625   VOIDSTAR closure;\r
626 } InputSource;\r
627 \r
628 InputSource *consoleInputSource;\r
629 \r
630 DCB dcb;\r
631 \r
632 /* forward */\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
634 VOID ConsoleCreate();\r
635 LRESULT CALLBACK\r
636   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);\r
640 LRESULT CALLBACK\r
641   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
643 void ParseIcsTextMenu(char *icsTextMenuString);\r
644 VOID PopUpMoveDialog(char firstchar);\r
645 VOID PopUpNameDialog(char firstchar);\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
647 \r
648 /* [AS] */\r
649 int NewGameFRC();\r
650 int GameListOptions();\r
651 \r
652 int dummy; // [HGM] for obsolete args\r
653 \r
654 HWND hwndMain = NULL;        /* root window*/\r
655 HWND hwndConsole = NULL;\r
656 HWND commentDialog = NULL;\r
657 HWND moveHistoryDialog = NULL;\r
658 HWND evalGraphDialog = NULL;\r
659 HWND engineOutputDialog = NULL;\r
660 HWND gameListDialog = NULL;\r
661 HWND editTagsDialog = NULL;\r
662 \r
663 int commentUp = FALSE;\r
664 \r
665 WindowPlacement wpMain;\r
666 WindowPlacement wpConsole;\r
667 WindowPlacement wpComment;\r
668 WindowPlacement wpMoveHistory;\r
669 WindowPlacement wpEvalGraph;\r
670 WindowPlacement wpEngineOutput;\r
671 WindowPlacement wpGameList;\r
672 WindowPlacement wpTags;\r
673 \r
674 VOID EngineOptionsPopup(); // [HGM] settings\r
675 \r
676 VOID GothicPopUp(char *title, VariantClass variant);\r
677 /*\r
678  * Setting "frozen" should disable all user input other than deleting\r
679  * the window.  We do this while engines are initializing themselves.\r
680  */\r
681 static int frozen = 0;\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];\r
683 void FreezeUI()\r
684 {\r
685   HMENU hmenu;\r
686   int i;\r
687 \r
688   if (frozen) return;\r
689   frozen = 1;\r
690   hmenu = GetMenu(hwndMain);\r
691   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
692     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
693   }\r
694   DrawMenuBar(hwndMain);\r
695 }\r
696 \r
697 /* Undo a FreezeUI */\r
698 void ThawUI()\r
699 {\r
700   HMENU hmenu;\r
701   int i;\r
702 \r
703   if (!frozen) return;\r
704   frozen = 0;\r
705   hmenu = GetMenu(hwndMain);\r
706   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
707     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
708   }\r
709   DrawMenuBar(hwndMain);\r
710 }\r
711 \r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
713 \r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
715 #ifdef JAWS\r
716 #include "jaws.c"\r
717 #else\r
718 #define JAWS_INIT\r
719 #define JAWS_ARGS\r
720 #define JAWS_ALT_INTERCEPT\r
721 #define JAWS_KB_NAVIGATION\r
722 #define JAWS_MENU_ITEMS\r
723 #define JAWS_SILENCE\r
724 #define JAWS_REPLAY\r
725 #define JAWS_ACCEL\r
726 #define JAWS_COPYRIGHT\r
727 #define JAWS_DELETE(X) X\r
728 #define SAYMACHINEMOVE()\r
729 #define SAY(X)\r
730 #endif\r
731 \r
732 /*---------------------------------------------------------------------------*\\r
733  *\r
734  * WinMain\r
735  *\r
736 \*---------------------------------------------------------------------------*/\r
737 \r
738 int APIENTRY\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
740         LPSTR lpCmdLine, int nCmdShow)\r
741 {\r
742   MSG msg;\r
743   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
744 //  INITCOMMONCONTROLSEX ex;\r
745 \r
746   debugFP = stderr;\r
747 \r
748   LoadLibrary("RICHED32.DLL");\r
749   consoleCF.cbSize = sizeof(CHARFORMAT);\r
750 \r
751   if (!InitApplication(hInstance)) {\r
752     return (FALSE);\r
753   }\r
754   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
755     return (FALSE);\r
756   }\r
757 \r
758   JAWS_INIT\r
759 \r
760 //  InitCommonControlsEx(&ex);\r
761   InitCommonControls();\r
762 \r
763   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
764   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
765   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
766 \r
767   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
768 \r
769   while (GetMessage(&msg, /* message structure */\r
770                     NULL, /* handle of window receiving the message */\r
771                     0,    /* lowest message to examine */\r
772                     0))   /* highest message to examine */\r
773     {\r
774 \r
775       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
776         // [HGM] navigate: switch between all windows with tab\r
777         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
778         int i, currentElement = 0;\r
779 \r
780         // first determine what element of the chain we come from (if any)\r
781         if(appData.icsActive) {\r
782             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
783             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
784         }\r
785         if(engineOutputDialog && EngineOutputIsUp()) {\r
786             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
787             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
788         }\r
789         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
790             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
791         }\r
792         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
793         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
794         if(msg.hwnd == e1)                 currentElement = 2; else\r
795         if(msg.hwnd == e2)                 currentElement = 3; else\r
796         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
797         if(msg.hwnd == mh)                currentElement = 4; else\r
798         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
799         if(msg.hwnd == hText)  currentElement = 5; else\r
800         if(msg.hwnd == hInput) currentElement = 6; else\r
801         for (i = 0; i < N_BUTTONS; i++) {\r
802             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
803         }\r
804 \r
805         // determine where to go to\r
806         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
807           do {\r
808             currentElement = (currentElement + direction) % 7;\r
809             switch(currentElement) {\r
810                 case 0:\r
811                   h = hwndMain; break; // passing this case always makes the loop exit\r
812                 case 1:\r
813                   h = buttonDesc[0].hwnd; break; // could be NULL\r
814                 case 2:\r
815                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
816                   h = e1; break;\r
817                 case 3:\r
818                   if(!EngineOutputIsUp()) continue;\r
819                   h = e2; break;\r
820                 case 4:\r
821                   if(!MoveHistoryIsUp()) continue;\r
822                   h = mh; break;\r
823 //              case 6: // input to eval graph does not seem to get here!\r
824 //                if(!EvalGraphIsUp()) continue;\r
825 //                h = evalGraphDialog; break;\r
826                 case 5:\r
827                   if(!appData.icsActive) continue;\r
828                   SAY("display");\r
829                   h = hText; break;\r
830                 case 6:\r
831                   if(!appData.icsActive) continue;\r
832                   SAY("input");\r
833                   h = hInput; break;\r
834             }\r
835           } while(h == 0);\r
836 \r
837           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
838           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
839           SetFocus(h);\r
840 \r
841           continue; // this message now has been processed\r
842         }\r
843       }\r
844 \r
845       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
846           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
847           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
848           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
849           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
850           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
851           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
852           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
854           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
855         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
856         for(i=0; i<MAX_CHAT; i++) \r
857             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
858                 done = 1; break;\r
859         }\r
860         if(done) continue; // [HGM] chat: end patch\r
861         TranslateMessage(&msg); /* Translates virtual key codes */\r
862         DispatchMessage(&msg);  /* Dispatches message to window */\r
863       }\r
864     }\r
865 \r
866 \r
867   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
868 }\r
869 \r
870 /*---------------------------------------------------------------------------*\\r
871  *\r
872  * Initialization functions\r
873  *\r
874 \*---------------------------------------------------------------------------*/\r
875 \r
876 void\r
877 SetUserLogo()\r
878 {   // update user logo if necessary\r
879     static char oldUserName[MSG_SIZ], *curName;\r
880 \r
881     if(appData.autoLogo) {\r
882           curName = UserName();\r
883           if(strcmp(curName, oldUserName)) {\r
884             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
885                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
886                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
887           }\r
888     }\r
889 }\r
890 \r
891 BOOL\r
892 InitApplication(HINSTANCE hInstance)\r
893 {\r
894   WNDCLASS wc;\r
895 \r
896   /* Fill in window class structure with parameters that describe the */\r
897   /* main window. */\r
898 \r
899   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
900   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
901   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
902   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
903   wc.hInstance     = hInstance;         /* Owner of this class */\r
904   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
905   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
906   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
907   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
908   wc.lpszClassName = szAppName;                 /* Name to register as */\r
909 \r
910   /* Register the window class and return success/failure code. */\r
911   if (!RegisterClass(&wc)) return FALSE;\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
914   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
915   wc.cbClsExtra    = 0;\r
916   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
917   wc.hInstance     = hInstance;\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
920   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
921   wc.lpszMenuName  = NULL;\r
922   wc.lpszClassName = szConsoleName;\r
923 \r
924   if (!RegisterClass(&wc)) return FALSE;\r
925   return TRUE;\r
926 }\r
927 \r
928 \r
929 /* Set by InitInstance, used by EnsureOnScreen */\r
930 int screenHeight, screenWidth;\r
931 \r
932 void\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
934 {\r
935 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
936   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
937   if (*x > screenWidth - 32) *x = 0;\r
938   if (*y > screenHeight - 32) *y = 0;\r
939   if (*x < minX) *x = minX;\r
940   if (*y < minY) *y = minY;\r
941 }\r
942 \r
943 BOOL\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
945 {\r
946   HWND hwnd; /* Main window handle. */\r
947   int ibs;\r
948   WINDOWPLACEMENT wp;\r
949   char *filepart;\r
950 \r
951   hInst = hInstance;    /* Store instance handle in our global variable */\r
952   programName = szAppName;\r
953 \r
954   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
955     *filepart = NULLCHAR;\r
956   } else {\r
957     GetCurrentDirectory(MSG_SIZ, installDir);\r
958   }\r
959   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
960   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
961   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
962   /* xboard, and older WinBoards, controlled the move sound with the\r
963      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
964      always turn the option on (so that the backend will call us),\r
965      then let the user turn the sound off by setting it to silence if\r
966      desired.  To accommodate old winboard.ini files saved by old\r
967      versions of WinBoard, we also turn off the sound if the option\r
968      was initially set to false. [HGM] taken out of InitAppData */\r
969   if (!appData.ringBellAfterMoves) {\r
970     sounds[(int)SoundMove].name = strdup("");\r
971     appData.ringBellAfterMoves = TRUE;\r
972   }\r
973   if (appData.debugMode) {\r
974     debugFP = fopen(appData.nameOfDebugFile, "w");\r
975     setbuf(debugFP, NULL);\r
976   }\r
977 \r
978   LoadLanguageFile(appData.language);\r
979 \r
980   InitBackEnd1();\r
981 \r
982 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
983 //  InitEngineUCI( installDir, &second );\r
984 \r
985   /* Create a main window for this application instance. */\r
986   hwnd = CreateWindow(szAppName, szTitle,\r
987                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
988                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
989                       NULL, NULL, hInstance, NULL);\r
990   hwndMain = hwnd;\r
991 \r
992   /* If window could not be created, return "failure" */\r
993   if (!hwnd) {\r
994     return (FALSE);\r
995   }\r
996 \r
997   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
998   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
999       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (first.programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1006         char buf[MSG_SIZ];\r
1007           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1008         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1009       }\r
1010   }\r
1011 \r
1012   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1013       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (second.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       char buf[MSG_SIZ];\r
1020       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1021         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1022         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023       } else\r
1024       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1025         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1026         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1027       }\r
1028   }\r
1029 \r
1030   SetUserLogo();\r
1031 \r
1032   iconWhite = LoadIcon(hInstance, "icon_white");\r
1033   iconBlack = LoadIcon(hInstance, "icon_black");\r
1034   iconCurrent = iconWhite;\r
1035   InitDrawingColors();\r
1036   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1037   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1038   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1039     /* Compute window size for each board size, and use the largest\r
1040        size that fits on this screen as the default. */\r
1041     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1042     if (boardSize == (BoardSize)-1 &&\r
1043         winH <= screenHeight\r
1044            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1045         && winW <= screenWidth) {\r
1046       boardSize = (BoardSize)ibs;\r
1047     }\r
1048   }\r
1049 \r
1050   InitDrawingSizes(boardSize, 0);\r
1051   TranslateMenus(1);\r
1052   InitMenuChecks();\r
1053   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1054 \r
1055   /* [AS] Load textures if specified */\r
1056   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1057   \r
1058   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1059       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1060       liteBackTextureMode = appData.liteBackTextureMode;\r
1061 \r
1062       if (liteBackTexture == NULL && appData.debugMode) {\r
1063           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1064       }\r
1065   }\r
1066   \r
1067   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1068       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1069       darkBackTextureMode = appData.darkBackTextureMode;\r
1070 \r
1071       if (darkBackTexture == NULL && appData.debugMode) {\r
1072           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1073       }\r
1074   }\r
1075 \r
1076   mysrandom( (unsigned) time(NULL) );\r
1077 \r
1078   /* [AS] Restore layout */\r
1079   if( wpMoveHistory.visible ) {\r
1080       MoveHistoryPopUp();\r
1081   }\r
1082 \r
1083   if( wpEvalGraph.visible ) {\r
1084       EvalGraphPopUp();\r
1085   }\r
1086 \r
1087   if( wpEngineOutput.visible ) {\r
1088       EngineOutputPopUp();\r
1089   }\r
1090 \r
1091   /* Make the window visible; update its client area; and return "success" */\r
1092   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1093   wp.length = sizeof(WINDOWPLACEMENT);\r
1094   wp.flags = 0;\r
1095   wp.showCmd = nCmdShow;\r
1096   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1097   wp.rcNormalPosition.left = wpMain.x;\r
1098   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1099   wp.rcNormalPosition.top = wpMain.y;\r
1100   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1101   SetWindowPlacement(hwndMain, &wp);\r
1102 \r
1103   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1104 \r
1105   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1106                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1107 \r
1108   if (hwndConsole) {\r
1109 #if AOT_CONSOLE\r
1110     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1111                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1112 #endif\r
1113     ShowWindow(hwndConsole, nCmdShow);\r
1114     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1115       char buf[MSG_SIZ], *p = buf, *q;\r
1116         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1117       do {\r
1118         q = strchr(p, ';');\r
1119         if(q) *q++ = 0;\r
1120         if(*p) ChatPopUp(p);\r
1121       } while(p=q);\r
1122     }\r
1123     SetActiveWindow(hwndConsole);\r
1124   }\r
1125   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1126   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1127 \r
1128   return TRUE;\r
1129 \r
1130 }\r
1131 \r
1132 VOID\r
1133 InitMenuChecks()\r
1134 {\r
1135   HMENU hmenu = GetMenu(hwndMain);\r
1136 \r
1137   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1138                         MF_BYCOMMAND|((appData.icsActive &&\r
1139                                        *appData.icsCommPort != NULLCHAR) ?\r
1140                                       MF_ENABLED : MF_GRAYED));\r
1141   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1142                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1143                                      MF_CHECKED : MF_UNCHECKED));\r
1144 }\r
1145 \r
1146 //---------------------------------------------------------------------------------------------------------\r
1147 \r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1149 #define XBOARD FALSE\r
1150 \r
1151 #define OPTCHAR "/"\r
1152 #define SEPCHAR "="\r
1153 \r
1154 #include "args.h"\r
1155 \r
1156 // front-end part of option handling\r
1157 \r
1158 VOID\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1160 {\r
1161   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1162   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1163   DeleteDC(hdc);\r
1164   lf->lfWidth = 0;\r
1165   lf->lfEscapement = 0;\r
1166   lf->lfOrientation = 0;\r
1167   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1168   lf->lfItalic = mfp->italic;\r
1169   lf->lfUnderline = mfp->underline;\r
1170   lf->lfStrikeOut = mfp->strikeout;\r
1171   lf->lfCharSet = mfp->charset;\r
1172   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1173   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1174   lf->lfQuality = DEFAULT_QUALITY;\r
1175   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1176     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1177 }\r
1178 \r
1179 void\r
1180 CreateFontInMF(MyFont *mf)\r
1181\r
1182   LFfromMFP(&mf->lf, &mf->mfp);\r
1183   if (mf->hf) DeleteObject(mf->hf);\r
1184   mf->hf = CreateFontIndirect(&mf->lf);\r
1185 }\r
1186 \r
1187 // [HGM] This platform-dependent table provides the location for storing the color info\r
1188 void *\r
1189 colorVariable[] = {\r
1190   &whitePieceColor, \r
1191   &blackPieceColor, \r
1192   &lightSquareColor,\r
1193   &darkSquareColor, \r
1194   &highlightSquareColor,\r
1195   &premoveHighlightColor,\r
1196   NULL,\r
1197   &consoleBackgroundColor,\r
1198   &appData.fontForeColorWhite,\r
1199   &appData.fontBackColorWhite,\r
1200   &appData.fontForeColorBlack,\r
1201   &appData.fontBackColorBlack,\r
1202   &appData.evalHistColorWhite,\r
1203   &appData.evalHistColorBlack,\r
1204   &appData.highlightArrowColor,\r
1205 };\r
1206 \r
1207 /* Command line font name parser.  NULL name means do nothing.\r
1208    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1209    For backward compatibility, syntax without the colon is also\r
1210    accepted, but font names with digits in them won't work in that case.\r
1211 */\r
1212 VOID\r
1213 ParseFontName(char *name, MyFontParams *mfp)\r
1214 {\r
1215   char *p, *q;\r
1216   if (name == NULL) return;\r
1217   p = name;\r
1218   q = strchr(p, ':');\r
1219   if (q) {\r
1220     if (q - p >= sizeof(mfp->faceName))\r
1221       ExitArgError(_("Font name too long:"), name);\r
1222     memcpy(mfp->faceName, p, q - p);\r
1223     mfp->faceName[q - p] = NULLCHAR;\r
1224     p = q + 1;\r
1225   } else {\r
1226     q = mfp->faceName;\r
1227     while (*p && !isdigit(*p)) {\r
1228       *q++ = *p++;\r
1229       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1230         ExitArgError(_("Font name too long:"), name);\r
1231     }\r
1232     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1233     *q = NULLCHAR;\r
1234   }\r
1235   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1236   mfp->pointSize = (float) atof(p);\r
1237   mfp->bold = (strchr(p, 'b') != NULL);\r
1238   mfp->italic = (strchr(p, 'i') != NULL);\r
1239   mfp->underline = (strchr(p, 'u') != NULL);\r
1240   mfp->strikeout = (strchr(p, 's') != NULL);\r
1241   mfp->charset = DEFAULT_CHARSET;\r
1242   q = strchr(p, 'c');\r
1243   if (q)\r
1244     mfp->charset = (BYTE) atoi(q+1);\r
1245 }\r
1246 \r
1247 void\r
1248 ParseFont(char *name, int number)\r
1249 { // wrapper to shield back-end from 'font'\r
1250   ParseFontName(name, &font[boardSize][number]->mfp);\r
1251 }\r
1252 \r
1253 void\r
1254 SetFontDefaults()\r
1255 { // in WB  we have a 2D array of fonts; this initializes their description\r
1256   int i, j;\r
1257   /* Point font array elements to structures and\r
1258      parse default font names */\r
1259   for (i=0; i<NUM_FONTS; i++) {\r
1260     for (j=0; j<NUM_SIZES; j++) {\r
1261       font[j][i] = &fontRec[j][i];\r
1262       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1263     }\r
1264   }\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFonts()\r
1269 { // here we create the actual fonts from the selected descriptions\r
1270   int i, j;\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       CreateFontInMF(font[j][i]);\r
1274     }\r
1275   }\r
1276 }\r
1277 /* Color name parser.\r
1278    X version accepts X color names, but this one\r
1279    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1280 COLORREF\r
1281 ParseColorName(char *name)\r
1282 {\r
1283   int red, green, blue, count;\r
1284   char buf[MSG_SIZ];\r
1285 \r
1286   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1287   if (count != 3) {\r
1288     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1289       &red, &green, &blue);\r
1290   }\r
1291   if (count != 3) {\r
1292     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1293     DisplayError(buf, 0);\r
1294     return RGB(0, 0, 0);\r
1295   }\r
1296   return PALETTERGB(red, green, blue);\r
1297 }\r
1298 \r
1299 void\r
1300 ParseColor(int n, char *name)\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string\r
1302   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1303 }\r
1304 \r
1305 void\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1307 {\r
1308   char *e = argValue;\r
1309   int eff = 0;\r
1310 \r
1311   while (*e) {\r
1312     if (*e == 'b')      eff |= CFE_BOLD;\r
1313     else if (*e == 'i') eff |= CFE_ITALIC;\r
1314     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1315     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1316     else if (*e == '#' || isdigit(*e)) break;\r
1317     e++;\r
1318   }\r
1319   *effects = eff;\r
1320   *color   = ParseColorName(e);\r
1321 }\r
1322 \r
1323 void\r
1324 ParseTextAttribs(ColorClass cc, char *s)\r
1325 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1326     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1327     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1328 }\r
1329 \r
1330 void\r
1331 ParseBoardSize(void *addr, char *name)\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1336         *(BoardSize *)addr = bs;\r
1337         return;\r
1338     }\r
1339     bs++;\r
1340   }\r
1341   ExitArgError(_("Unrecognized board size value"), name);\r
1342 }\r
1343 \r
1344 void\r
1345 LoadAllSounds()\r
1346 { // [HGM] import name from appData first\r
1347   ColorClass cc;\r
1348   SoundClass sc;\r
1349   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1350     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1351     textAttribs[cc].sound.data = NULL;\r
1352     MyLoadSound(&textAttribs[cc].sound);\r
1353   }\r
1354   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1355     textAttribs[cc].sound.name = strdup("");\r
1356     textAttribs[cc].sound.data = NULL;\r
1357   }\r
1358   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1359     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1360     sounds[sc].data = NULL;\r
1361     MyLoadSound(&sounds[sc]);\r
1362   }\r
1363 }\r
1364 \r
1365 void\r
1366 SetCommPortDefaults()\r
1367 {\r
1368    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1369   dcb.DCBlength = sizeof(DCB);\r
1370   dcb.BaudRate = 9600;\r
1371   dcb.fBinary = TRUE;\r
1372   dcb.fParity = FALSE;\r
1373   dcb.fOutxCtsFlow = FALSE;\r
1374   dcb.fOutxDsrFlow = FALSE;\r
1375   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1376   dcb.fDsrSensitivity = FALSE;\r
1377   dcb.fTXContinueOnXoff = TRUE;\r
1378   dcb.fOutX = FALSE;\r
1379   dcb.fInX = FALSE;\r
1380   dcb.fNull = FALSE;\r
1381   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1382   dcb.fAbortOnError = FALSE;\r
1383   dcb.ByteSize = 7;\r
1384   dcb.Parity = SPACEPARITY;\r
1385   dcb.StopBits = ONESTOPBIT;\r
1386 }\r
1387 \r
1388 // [HGM] args: these three cases taken out to stay in front-end\r
1389 void\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1391 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1392         // while the curent board size determines the element. This system should be ported to XBoard.\r
1393         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1394         int bs;\r
1395         for (bs=0; bs<NUM_SIZES; bs++) {\r
1396           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1397           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1398           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1399             ad->argName, mfp->faceName, mfp->pointSize,\r
1400             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1401             mfp->bold ? "b" : "",\r
1402             mfp->italic ? "i" : "",\r
1403             mfp->underline ? "u" : "",\r
1404             mfp->strikeout ? "s" : "",\r
1405             (int)mfp->charset);\r
1406         }\r
1407       }\r
1408 \r
1409 void\r
1410 ExportSounds()\r
1411 { // [HGM] copy the names from the internal WB variables to appData\r
1412   ColorClass cc;\r
1413   SoundClass sc;\r
1414   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1415     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1416   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1417     (&appData.soundMove)[sc] = sounds[sc].name;\r
1418 }\r
1419 \r
1420 void\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1422 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1423         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1424         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1425           (ta->effects & CFE_BOLD) ? "b" : "",\r
1426           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1427           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1428           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1429           (ta->effects) ? " " : "",\r
1430           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1431       }\r
1432 \r
1433 void\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1436         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1437         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1438           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1439 }\r
1440 \r
1441 void\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1444   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseCommPortSettings(char *s)\r
1449 { // wrapper to keep dcb from back-end\r
1450   ParseCommSettings(s, &dcb);\r
1451 }\r
1452 \r
1453 void\r
1454 GetWindowCoords()\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1456   GetActualPlacement(hwndMain, &wpMain);\r
1457   GetActualPlacement(hwndConsole, &wpConsole);\r
1458   GetActualPlacement(commentDialog, &wpComment);\r
1459   GetActualPlacement(editTagsDialog, &wpTags);\r
1460   GetActualPlacement(gameListDialog, &wpGameList);\r
1461   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1462   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1463   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1464 }\r
1465 \r
1466 void\r
1467 PrintCommPortSettings(FILE *f, char *name)\r
1468 { // wrapper to shield back-end from DCB\r
1469       PrintCommSettings(f, name, &dcb);\r
1470 }\r
1471 \r
1472 int\r
1473 MySearchPath(char *installDir, char *name, char *fullname)\r
1474 {\r
1475   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1476   if(name[0]== '%') {\r
1477     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1478     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1479       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1480       *strchr(buf, '%') = 0;\r
1481       strcat(fullname, getenv(buf));\r
1482       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1483     }\r
1484     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1485     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1486     return (int) strlen(fullname);\r
1487   }\r
1488   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1489 }\r
1490 \r
1491 int\r
1492 MyGetFullPathName(char *name, char *fullname)\r
1493 {\r
1494   char *dummy;\r
1495   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1496 }\r
1497 \r
1498 int\r
1499 MainWindowUp()\r
1500 { // [HGM] args: allows testing if main window is realized from back-end\r
1501   return hwndMain != NULL;\r
1502 }\r
1503 \r
1504 void\r
1505 PopUpStartupDialog()\r
1506 {\r
1507     FARPROC lpProc;\r
1508     \r
1509     LoadLanguageFile(appData.language);\r
1510     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1511     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1512     FreeProcInstance(lpProc);\r
1513 }\r
1514 \r
1515 /*---------------------------------------------------------------------------*\\r
1516  *\r
1517  * GDI board drawing routines\r
1518  *\r
1519 \*---------------------------------------------------------------------------*/\r
1520 \r
1521 /* [AS] Draw square using background texture */\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1523 {\r
1524     XFORM   x;\r
1525 \r
1526     if( mode == 0 ) {\r
1527         return; /* Should never happen! */\r
1528     }\r
1529 \r
1530     SetGraphicsMode( dst, GM_ADVANCED );\r
1531 \r
1532     switch( mode ) {\r
1533     case 1:\r
1534         /* Identity */\r
1535         break;\r
1536     case 2:\r
1537         /* X reflection */\r
1538         x.eM11 = -1.0;\r
1539         x.eM12 = 0;\r
1540         x.eM21 = 0;\r
1541         x.eM22 = 1.0;\r
1542         x.eDx = (FLOAT) dw + dx - 1;\r
1543         x.eDy = 0;\r
1544         dx = 0;\r
1545         SetWorldTransform( dst, &x );\r
1546         break;\r
1547     case 3:\r
1548         /* Y reflection */\r
1549         x.eM11 = 1.0;\r
1550         x.eM12 = 0;\r
1551         x.eM21 = 0;\r
1552         x.eM22 = -1.0;\r
1553         x.eDx = 0;\r
1554         x.eDy = (FLOAT) dh + dy - 1;\r
1555         dy = 0;\r
1556         SetWorldTransform( dst, &x );\r
1557         break;\r
1558     case 4:\r
1559         /* X/Y flip */\r
1560         x.eM11 = 0;\r
1561         x.eM12 = 1.0;\r
1562         x.eM21 = 1.0;\r
1563         x.eM22 = 0;\r
1564         x.eDx = (FLOAT) dx;\r
1565         x.eDy = (FLOAT) dy;\r
1566         dx = 0;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     }\r
1571 \r
1572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1573 \r
1574     x.eM11 = 1.0;\r
1575     x.eM12 = 0;\r
1576     x.eM21 = 0;\r
1577     x.eM22 = 1.0;\r
1578     x.eDx = 0;\r
1579     x.eDy = 0;\r
1580     SetWorldTransform( dst, &x );\r
1581 \r
1582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1583 }\r
1584 \r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1586 enum {\r
1587     PM_WP = (int) WhitePawn, \r
1588     PM_WN = (int) WhiteKnight, \r
1589     PM_WB = (int) WhiteBishop, \r
1590     PM_WR = (int) WhiteRook, \r
1591     PM_WQ = (int) WhiteQueen, \r
1592     PM_WF = (int) WhiteFerz, \r
1593     PM_WW = (int) WhiteWazir, \r
1594     PM_WE = (int) WhiteAlfil, \r
1595     PM_WM = (int) WhiteMan, \r
1596     PM_WO = (int) WhiteCannon, \r
1597     PM_WU = (int) WhiteUnicorn, \r
1598     PM_WH = (int) WhiteNightrider, \r
1599     PM_WA = (int) WhiteAngel, \r
1600     PM_WC = (int) WhiteMarshall, \r
1601     PM_WAB = (int) WhiteCardinal, \r
1602     PM_WD = (int) WhiteDragon, \r
1603     PM_WL = (int) WhiteLance, \r
1604     PM_WS = (int) WhiteCobra, \r
1605     PM_WV = (int) WhiteFalcon, \r
1606     PM_WSG = (int) WhiteSilver, \r
1607     PM_WG = (int) WhiteGrasshopper, \r
1608     PM_WK = (int) WhiteKing,\r
1609     PM_BP = (int) BlackPawn, \r
1610     PM_BN = (int) BlackKnight, \r
1611     PM_BB = (int) BlackBishop, \r
1612     PM_BR = (int) BlackRook, \r
1613     PM_BQ = (int) BlackQueen, \r
1614     PM_BF = (int) BlackFerz, \r
1615     PM_BW = (int) BlackWazir, \r
1616     PM_BE = (int) BlackAlfil, \r
1617     PM_BM = (int) BlackMan,\r
1618     PM_BO = (int) BlackCannon, \r
1619     PM_BU = (int) BlackUnicorn, \r
1620     PM_BH = (int) BlackNightrider, \r
1621     PM_BA = (int) BlackAngel, \r
1622     PM_BC = (int) BlackMarshall, \r
1623     PM_BG = (int) BlackGrasshopper, \r
1624     PM_BAB = (int) BlackCardinal,\r
1625     PM_BD = (int) BlackDragon,\r
1626     PM_BL = (int) BlackLance,\r
1627     PM_BS = (int) BlackCobra,\r
1628     PM_BV = (int) BlackFalcon,\r
1629     PM_BSG = (int) BlackSilver,\r
1630     PM_BK = (int) BlackKing\r
1631 };\r
1632 \r
1633 static HFONT hPieceFont = NULL;\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];\r
1636 static int fontBitmapSquareSize = 0;\r
1637 static char pieceToFontChar[(int) EmptySquare] =\r
1638                               { 'p', 'n', 'b', 'r', 'q', \r
1639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1640                       'k', 'o', 'm', 'v', 't', 'w', \r
1641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1642                                                               'l' };\r
1643 \r
1644 extern BOOL SetCharTable( char *table, const char * map );\r
1645 /* [HGM] moved to backend.c */\r
1646 \r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1648 {\r
1649     HBRUSH hbrush;\r
1650     BYTE r1 = GetRValue( color );\r
1651     BYTE g1 = GetGValue( color );\r
1652     BYTE b1 = GetBValue( color );\r
1653     BYTE r2 = r1 / 2;\r
1654     BYTE g2 = g1 / 2;\r
1655     BYTE b2 = b1 / 2;\r
1656     RECT rc;\r
1657 \r
1658     /* Create a uniform background first */\r
1659     hbrush = CreateSolidBrush( color );\r
1660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1661     FillRect( hdc, &rc, hbrush );\r
1662     DeleteObject( hbrush );\r
1663     \r
1664     if( mode == 1 ) {\r
1665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1666         int steps = squareSize / 2;\r
1667         int i;\r
1668 \r
1669         for( i=0; i<steps; i++ ) {\r
1670             BYTE r = r1 - (r1-r2) * i / steps;\r
1671             BYTE g = g1 - (g1-g2) * i / steps;\r
1672             BYTE b = b1 - (b1-b2) * i / steps;\r
1673 \r
1674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1676             FillRect( hdc, &rc, hbrush );\r
1677             DeleteObject(hbrush);\r
1678         }\r
1679     }\r
1680     else if( mode == 2 ) {\r
1681         /* Diagonal gradient, good more or less for every piece */\r
1682         POINT triangle[3];\r
1683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1684         HBRUSH hbrush_old;\r
1685         int steps = squareSize;\r
1686         int i;\r
1687 \r
1688         triangle[0].x = squareSize - steps;\r
1689         triangle[0].y = squareSize;\r
1690         triangle[1].x = squareSize;\r
1691         triangle[1].y = squareSize;\r
1692         triangle[2].x = squareSize;\r
1693         triangle[2].y = squareSize - steps;\r
1694 \r
1695         for( i=0; i<steps; i++ ) {\r
1696             BYTE r = r1 - (r1-r2) * i / steps;\r
1697             BYTE g = g1 - (g1-g2) * i / steps;\r
1698             BYTE b = b1 - (b1-b2) * i / steps;\r
1699 \r
1700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1701             hbrush_old = SelectObject( hdc, hbrush );\r
1702             Polygon( hdc, triangle, 3 );\r
1703             SelectObject( hdc, hbrush_old );\r
1704             DeleteObject(hbrush);\r
1705             triangle[0].x++;\r
1706             triangle[2].y++;\r
1707         }\r
1708 \r
1709         SelectObject( hdc, hpen );\r
1710     }\r
1711 }\r
1712 \r
1713 /*\r
1714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1715     seems to work ok. The main problem here is to find the "inside" of a chess\r
1716     piece: follow the steps as explained below.\r
1717 */\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1719 {\r
1720     HBITMAP hbm;\r
1721     HBITMAP hbm_old;\r
1722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1723     RECT rc;\r
1724     SIZE sz;\r
1725     POINT pt;\r
1726     int backColor = whitePieceColor; \r
1727     int foreColor = blackPieceColor;\r
1728     \r
1729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1730         backColor = appData.fontBackColorWhite;\r
1731         foreColor = appData.fontForeColorWhite;\r
1732     }\r
1733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1734         backColor = appData.fontBackColorBlack;\r
1735         foreColor = appData.fontForeColorBlack;\r
1736     }\r
1737 \r
1738     /* Mask */\r
1739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1740 \r
1741     hbm_old = SelectObject( hdc, hbm );\r
1742 \r
1743     rc.left = 0;\r
1744     rc.top = 0;\r
1745     rc.right = squareSize;\r
1746     rc.bottom = squareSize;\r
1747 \r
1748     /* Step 1: background is now black */\r
1749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1750 \r
1751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1752 \r
1753     pt.x = (squareSize - sz.cx) / 2;\r
1754     pt.y = (squareSize - sz.cy) / 2;\r
1755 \r
1756     SetBkMode( hdc, TRANSPARENT );\r
1757     SetTextColor( hdc, chroma );\r
1758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1760 \r
1761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1762     /* Step 3: the area outside the piece is filled with white */\r
1763 //    FloodFill( hdc, 0, 0, chroma );\r
1764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1769     /* \r
1770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1771         but if the start point is not inside the piece we're lost!\r
1772         There should be a better way to do this... if we could create a region or path\r
1773         from the fill operation we would be fine for example.\r
1774     */\r
1775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1777 \r
1778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1781 \r
1782         SelectObject( dc2, bm2 );\r
1783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788 \r
1789         DeleteDC( dc2 );\r
1790         DeleteObject( bm2 );\r
1791     }\r
1792 \r
1793     SetTextColor( hdc, 0 );\r
1794     /* \r
1795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1796         draw the piece again in black for safety.\r
1797     */\r
1798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1799 \r
1800     SelectObject( hdc, hbm_old );\r
1801 \r
1802     if( hPieceMask[index] != NULL ) {\r
1803         DeleteObject( hPieceMask[index] );\r
1804     }\r
1805 \r
1806     hPieceMask[index] = hbm;\r
1807 \r
1808     /* Face */\r
1809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1810 \r
1811     SelectObject( hdc, hbm );\r
1812 \r
1813     {\r
1814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1817 \r
1818         SelectObject( dc1, hPieceMask[index] );\r
1819         SelectObject( dc2, bm2 );\r
1820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1822         \r
1823         /* \r
1824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1825             the piece background and deletes (makes transparent) the rest.\r
1826             Thanks to that mask, we are free to paint the background with the greates\r
1827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1828             We use this, to make gradients and give the pieces a "roundish" look.\r
1829         */\r
1830         SetPieceBackground( hdc, backColor, 2 );\r
1831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteDC( dc1 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, foreColor );\r
1839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1840 \r
1841     SelectObject( hdc, hbm_old );\r
1842 \r
1843     if( hPieceFace[index] != NULL ) {\r
1844         DeleteObject( hPieceFace[index] );\r
1845     }\r
1846 \r
1847     hPieceFace[index] = hbm;\r
1848 }\r
1849 \r
1850 static int TranslatePieceToFontPiece( int piece )\r
1851 {\r
1852     switch( piece ) {\r
1853     case BlackPawn:\r
1854         return PM_BP;\r
1855     case BlackKnight:\r
1856         return PM_BN;\r
1857     case BlackBishop:\r
1858         return PM_BB;\r
1859     case BlackRook:\r
1860         return PM_BR;\r
1861     case BlackQueen:\r
1862         return PM_BQ;\r
1863     case BlackKing:\r
1864         return PM_BK;\r
1865     case WhitePawn:\r
1866         return PM_WP;\r
1867     case WhiteKnight:\r
1868         return PM_WN;\r
1869     case WhiteBishop:\r
1870         return PM_WB;\r
1871     case WhiteRook:\r
1872         return PM_WR;\r
1873     case WhiteQueen:\r
1874         return PM_WQ;\r
1875     case WhiteKing:\r
1876         return PM_WK;\r
1877 \r
1878     case BlackAngel:\r
1879         return PM_BA;\r
1880     case BlackMarshall:\r
1881         return PM_BC;\r
1882     case BlackFerz:\r
1883         return PM_BF;\r
1884     case BlackNightrider:\r
1885         return PM_BH;\r
1886     case BlackAlfil:\r
1887         return PM_BE;\r
1888     case BlackWazir:\r
1889         return PM_BW;\r
1890     case BlackUnicorn:\r
1891         return PM_BU;\r
1892     case BlackCannon:\r
1893         return PM_BO;\r
1894     case BlackGrasshopper:\r
1895         return PM_BG;\r
1896     case BlackMan:\r
1897         return PM_BM;\r
1898     case BlackSilver:\r
1899         return PM_BSG;\r
1900     case BlackLance:\r
1901         return PM_BL;\r
1902     case BlackFalcon:\r
1903         return PM_BV;\r
1904     case BlackCobra:\r
1905         return PM_BS;\r
1906     case BlackCardinal:\r
1907         return PM_BAB;\r
1908     case BlackDragon:\r
1909         return PM_BD;\r
1910 \r
1911     case WhiteAngel:\r
1912         return PM_WA;\r
1913     case WhiteMarshall:\r
1914         return PM_WC;\r
1915     case WhiteFerz:\r
1916         return PM_WF;\r
1917     case WhiteNightrider:\r
1918         return PM_WH;\r
1919     case WhiteAlfil:\r
1920         return PM_WE;\r
1921     case WhiteWazir:\r
1922         return PM_WW;\r
1923     case WhiteUnicorn:\r
1924         return PM_WU;\r
1925     case WhiteCannon:\r
1926         return PM_WO;\r
1927     case WhiteGrasshopper:\r
1928         return PM_WG;\r
1929     case WhiteMan:\r
1930         return PM_WM;\r
1931     case WhiteSilver:\r
1932         return PM_WSG;\r
1933     case WhiteLance:\r
1934         return PM_WL;\r
1935     case WhiteFalcon:\r
1936         return PM_WV;\r
1937     case WhiteCobra:\r
1938         return PM_WS;\r
1939     case WhiteCardinal:\r
1940         return PM_WAB;\r
1941     case WhiteDragon:\r
1942         return PM_WD;\r
1943     }\r
1944 \r
1945     return 0;\r
1946 }\r
1947 \r
1948 void CreatePiecesFromFont()\r
1949 {\r
1950     LOGFONT lf;\r
1951     HDC hdc_window = NULL;\r
1952     HDC hdc = NULL;\r
1953     HFONT hfont_old;\r
1954     int fontHeight;\r
1955     int i;\r
1956 \r
1957     if( fontBitmapSquareSize < 0 ) {\r
1958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1959         return;\r
1960     }\r
1961 \r
1962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1963         fontBitmapSquareSize = -1;\r
1964         return;\r
1965     }\r
1966 \r
1967     if( fontBitmapSquareSize != squareSize ) {\r
1968         hdc_window = GetDC( hwndMain );\r
1969         hdc = CreateCompatibleDC( hdc_window );\r
1970 \r
1971         if( hPieceFont != NULL ) {\r
1972             DeleteObject( hPieceFont );\r
1973         }\r
1974         else {\r
1975             for( i=0; i<=(int)BlackKing; i++ ) {\r
1976                 hPieceMask[i] = NULL;\r
1977                 hPieceFace[i] = NULL;\r
1978             }\r
1979         }\r
1980 \r
1981         fontHeight = 75;\r
1982 \r
1983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1984             fontHeight = appData.fontPieceSize;\r
1985         }\r
1986 \r
1987         fontHeight = (fontHeight * squareSize) / 100;\r
1988 \r
1989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1990         lf.lfWidth = 0;\r
1991         lf.lfEscapement = 0;\r
1992         lf.lfOrientation = 0;\r
1993         lf.lfWeight = FW_NORMAL;\r
1994         lf.lfItalic = 0;\r
1995         lf.lfUnderline = 0;\r
1996         lf.lfStrikeOut = 0;\r
1997         lf.lfCharSet = DEFAULT_CHARSET;\r
1998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2000         lf.lfQuality = PROOF_QUALITY;\r
2001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2004 \r
2005         hPieceFont = CreateFontIndirect( &lf );\r
2006 \r
2007         if( hPieceFont == NULL ) {\r
2008             fontBitmapSquareSize = -2;\r
2009         }\r
2010         else {\r
2011             /* Setup font-to-piece character table */\r
2012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2013                 /* No (or wrong) global settings, try to detect the font */\r
2014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2015                     /* Alpha */\r
2016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2017                 }\r
2018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2019                     /* DiagramTT* family */\r
2020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2021                 }\r
2022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2023                     /* Fairy symbols */\r
2024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2025                 }\r
2026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2027                     /* Good Companion (Some characters get warped as literal :-( */\r
2028                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2030                     SetCharTable(pieceToFontChar, s);\r
2031                 }\r
2032                 else {\r
2033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2035                 }\r
2036             }\r
2037 \r
2038             /* Create bitmaps */\r
2039             hfont_old = SelectObject( hdc, hPieceFont );\r
2040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2043 \r
2044             SelectObject( hdc, hfont_old );\r
2045 \r
2046             fontBitmapSquareSize = squareSize;\r
2047         }\r
2048     }\r
2049 \r
2050     if( hdc != NULL ) {\r
2051         DeleteDC( hdc );\r
2052     }\r
2053 \r
2054     if( hdc_window != NULL ) {\r
2055         ReleaseDC( hwndMain, hdc_window );\r
2056     }\r
2057 }\r
2058 \r
2059 HBITMAP\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2061 {\r
2062   char name[128];\r
2063 \r
2064     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2065   if (gameInfo.event &&\r
2066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2067       strcmp(name, "k80s") == 0) {\r
2068     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2069   }\r
2070   return LoadBitmap(hinst, name);\r
2071 }\r
2072 \r
2073 \r
2074 /* Insert a color into the program's logical palette\r
2075    structure.  This code assumes the given color is\r
2076    the result of the RGB or PALETTERGB macro, and it\r
2077    knows how those macros work (which is documented).\r
2078 */\r
2079 VOID\r
2080 InsertInPalette(COLORREF color)\r
2081 {\r
2082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2083 \r
2084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2085     DisplayFatalError(_("Too many colors"), 0, 1);\r
2086     pLogPal->palNumEntries--;\r
2087     return;\r
2088   }\r
2089 \r
2090   pe->peFlags = (char) 0;\r
2091   pe->peRed = (char) (0xFF & color);\r
2092   pe->peGreen = (char) (0xFF & (color >> 8));\r
2093   pe->peBlue = (char) (0xFF & (color >> 16));\r
2094   return;\r
2095 }\r
2096 \r
2097 \r
2098 VOID\r
2099 InitDrawingColors()\r
2100 {\r
2101   if (pLogPal == NULL) {\r
2102     /* Allocate enough memory for a logical palette with\r
2103      * PALETTESIZE entries and set the size and version fields\r
2104      * of the logical palette structure.\r
2105      */\r
2106     pLogPal = (NPLOGPALETTE)\r
2107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2109     pLogPal->palVersion    = 0x300;\r
2110   }\r
2111   pLogPal->palNumEntries = 0;\r
2112 \r
2113   InsertInPalette(lightSquareColor);\r
2114   InsertInPalette(darkSquareColor);\r
2115   InsertInPalette(whitePieceColor);\r
2116   InsertInPalette(blackPieceColor);\r
2117   InsertInPalette(highlightSquareColor);\r
2118   InsertInPalette(premoveHighlightColor);\r
2119 \r
2120   /*  create a logical color palette according the information\r
2121    *  in the LOGPALETTE structure.\r
2122    */\r
2123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2124 \r
2125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2132   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2133   /* [AS] Force rendering of the font-based pieces */\r
2134   if( fontBitmapSquareSize > 0 ) {\r
2135     fontBitmapSquareSize = 0;\r
2136   }\r
2137 }\r
2138 \r
2139 \r
2140 int\r
2141 BoardWidth(int boardSize, int n)\r
2142 { /* [HGM] argument n added to allow different width and height */\r
2143   int lineGap = sizeInfo[boardSize].lineGap;\r
2144 \r
2145   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2146       lineGap = appData.overrideLineGap;\r
2147   }\r
2148 \r
2149   return (n + 1) * lineGap +\r
2150           n * sizeInfo[boardSize].squareSize;\r
2151 }\r
2152 \r
2153 /* Respond to board resize by dragging edge */\r
2154 VOID\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2156 {\r
2157   BoardSize newSize = NUM_SIZES - 1;\r
2158   static int recurse = 0;\r
2159   if (IsIconic(hwndMain)) return;\r
2160   if (recurse > 0) return;\r
2161   recurse++;\r
2162   while (newSize > 0) {\r
2163         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2164         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2165            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2166     newSize--;\r
2167   } \r
2168   boardSize = newSize;\r
2169   InitDrawingSizes(boardSize, flags);\r
2170   recurse--;\r
2171 }\r
2172 \r
2173 \r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2175 \r
2176 VOID\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)\r
2178 {\r
2179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2180   ChessSquare piece;\r
2181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2182   HDC hdc;\r
2183   SIZE clockSize, messageSize;\r
2184   HFONT oldFont;\r
2185   char buf[MSG_SIZ];\r
2186   char *str;\r
2187   HMENU hmenu = GetMenu(hwndMain);\r
2188   RECT crect, wrect, oldRect;\r
2189   int offby;\r
2190   LOGBRUSH logbrush;\r
2191 \r
2192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2194 \r
2195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2197 \r
2198   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2199   oldRect.top = wpMain.y;\r
2200   oldRect.right = wpMain.x + wpMain.width;\r
2201   oldRect.bottom = wpMain.y + wpMain.height;\r
2202 \r
2203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2204   smallLayout = sizeInfo[boardSize].smallLayout;\r
2205   squareSize = sizeInfo[boardSize].squareSize;\r
2206   lineGap = sizeInfo[boardSize].lineGap;\r
2207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   if (tinyLayout != oldTinyLayout) {\r
2214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2215     if (tinyLayout) {\r
2216       style &= ~WS_SYSMENU;\r
2217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2218                  "&Minimize\tCtrl+F4");\r
2219     } else {\r
2220       style |= WS_SYSMENU;\r
2221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2222     }\r
2223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2224 \r
2225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2227         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2228     }\r
2229     DrawMenuBar(hwndMain);\r
2230   }\r
2231 \r
2232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2234 \r
2235   /* Get text area sizes */\r
2236   hdc = GetDC(hwndMain);\r
2237   if (appData.clockMode) {\r
2238     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2239   } else {\r
2240     snprintf(buf, MSG_SIZ, _("White"));\r
2241   }\r
2242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2245   str = _("We only care about the height here");\r
2246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2247   SelectObject(hdc, oldFont);\r
2248   ReleaseDC(hwndMain, hdc);\r
2249 \r
2250   /* Compute where everything goes */\r
2251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2252         /* [HGM] logo: if either logo is on, reserve space for it */\r
2253         logoHeight =  2*clockSize.cy;\r
2254         leftLogoRect.left   = OUTER_MARGIN;\r
2255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2256         leftLogoRect.top    = OUTER_MARGIN;\r
2257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2258 \r
2259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2261         rightLogoRect.top    = OUTER_MARGIN;\r
2262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2263 \r
2264 \r
2265     whiteRect.left = leftLogoRect.right;\r
2266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2267     whiteRect.top = OUTER_MARGIN;\r
2268     whiteRect.bottom = whiteRect.top + logoHeight;\r
2269 \r
2270     blackRect.right = rightLogoRect.left;\r
2271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2272     blackRect.top = whiteRect.top;\r
2273     blackRect.bottom = whiteRect.bottom;\r
2274   } else {\r
2275     whiteRect.left = OUTER_MARGIN;\r
2276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2277     whiteRect.top = OUTER_MARGIN;\r
2278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2279 \r
2280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2282     blackRect.top = whiteRect.top;\r
2283     blackRect.bottom = whiteRect.bottom;\r
2284 \r
2285     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2286   }\r
2287 \r
2288   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2289   if (appData.showButtonBar) {\r
2290     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2291       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2292   } else {\r
2293     messageRect.right = OUTER_MARGIN + boardWidth;\r
2294   }\r
2295   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2296   messageRect.bottom = messageRect.top + messageSize.cy;\r
2297 \r
2298   boardRect.left = OUTER_MARGIN;\r
2299   boardRect.right = boardRect.left + boardWidth;\r
2300   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2301   boardRect.bottom = boardRect.top + boardHeight;\r
2302 \r
2303   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2304   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2305   oldBoardSize = boardSize;\r
2306   oldTinyLayout = tinyLayout;\r
2307   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2308   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2309     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2310   winW *= 1 + twoBoards;\r
2311   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2312   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2313   wpMain.height = winH; //       without disturbing window attachments\r
2314   GetWindowRect(hwndMain, &wrect);\r
2315   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2316                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2317 \r
2318   // [HGM] placement: let attached windows follow size change.\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2324 \r
2325   /* compensate if menu bar wrapped */\r
2326   GetClientRect(hwndMain, &crect);\r
2327   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2328   wpMain.height += offby;\r
2329   switch (flags) {\r
2330   case WMSZ_TOPLEFT:\r
2331     SetWindowPos(hwndMain, NULL, \r
2332                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2333                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2334     break;\r
2335 \r
2336   case WMSZ_TOPRIGHT:\r
2337   case WMSZ_TOP:\r
2338     SetWindowPos(hwndMain, NULL, \r
2339                  wrect.left, wrect.bottom - wpMain.height, \r
2340                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2341     break;\r
2342 \r
2343   case WMSZ_BOTTOMLEFT:\r
2344   case WMSZ_LEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.top, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_BOTTOMRIGHT:\r
2351   case WMSZ_BOTTOM:\r
2352   case WMSZ_RIGHT:\r
2353   default:\r
2354     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2356     break;\r
2357   }\r
2358 \r
2359   hwndPause = NULL;\r
2360   for (i = 0; i < N_BUTTONS; i++) {\r
2361     if (buttonDesc[i].hwnd != NULL) {\r
2362       DestroyWindow(buttonDesc[i].hwnd);\r
2363       buttonDesc[i].hwnd = NULL;\r
2364     }\r
2365     if (appData.showButtonBar) {\r
2366       buttonDesc[i].hwnd =\r
2367         CreateWindow("BUTTON", buttonDesc[i].label,\r
2368                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2369                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2370                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2371                      (HMENU) buttonDesc[i].id,\r
2372                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2373       if (tinyLayout) {\r
2374         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2375                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2376                     MAKELPARAM(FALSE, 0));\r
2377       }\r
2378       if (buttonDesc[i].id == IDM_Pause)\r
2379         hwndPause = buttonDesc[i].hwnd;\r
2380       buttonDesc[i].wndproc = (WNDPROC)\r
2381         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2382     }\r
2383   }\r
2384   if (gridPen != NULL) DeleteObject(gridPen);\r
2385   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2386   if (premovePen != NULL) DeleteObject(premovePen);\r
2387   if (lineGap != 0) {\r
2388     logbrush.lbStyle = BS_SOLID;\r
2389     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2390     gridPen =\r
2391       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2392                    lineGap, &logbrush, 0, NULL);\r
2393     logbrush.lbColor = highlightSquareColor;\r
2394     highlightPen =\r
2395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2396                    lineGap, &logbrush, 0, NULL);\r
2397 \r
2398     logbrush.lbColor = premoveHighlightColor; \r
2399     premovePen =\r
2400       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2401                    lineGap, &logbrush, 0, NULL);\r
2402 \r
2403     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2404     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2405       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2406       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2407         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2408       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2409         BOARD_WIDTH * (squareSize + lineGap);\r
2410       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2411     }\r
2412     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2414       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2415         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2416         lineGap / 2 + (i * (squareSize + lineGap));\r
2417       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2418         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2419       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2420     }\r
2421   }\r
2422 \r
2423   /* [HGM] Licensing requirement */\r
2424 #ifdef GOTHIC\r
2425   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2426 #endif\r
2427 #ifdef FALCON\r
2428   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2429 #endif\r
2430   GothicPopUp( "", VariantNormal);\r
2431 \r
2432 \r
2433 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2434 \r
2435   /* Load piece bitmaps for this board size */\r
2436   for (i=0; i<=2; i++) {\r
2437     for (piece = WhitePawn;\r
2438          (int) piece < (int) BlackPawn;\r
2439          piece = (ChessSquare) ((int) piece + 1)) {\r
2440       if (pieceBitmap[i][piece] != NULL)\r
2441         DeleteObject(pieceBitmap[i][piece]);\r
2442     }\r
2443   }\r
2444 \r
2445   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2446   // Orthodox Chess pieces\r
2447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2451   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2452   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2453   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2454   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2455   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2456   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2457   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2458   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2459   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2460   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2462   if( gameInfo.variant == VariantShogi && (squareSize==72 || squareSize==49)) {\r
2463     // in Shogi, Hijack the unused Queen for Lance\r
2464     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2465     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2466     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2467   } else {\r
2468     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2469     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2470     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2471   }\r
2472 \r
2473   if(squareSize <= 72 && squareSize >= 33) { \r
2474     /* A & C are available in most sizes now */\r
2475     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2476       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2477       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2478       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2479       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2480       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2481       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2482       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2483       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2484       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2485       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2486       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2487       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2488     } else { // Smirf-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2492     }\r
2493     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2494       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497     } else { // WinBoard standard\r
2498       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2499       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2500       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2501     }\r
2502   }\r
2503 \r
2504 \r
2505   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2506     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2507     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2508     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2509     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2510     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2511     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2512     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2513     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2514     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2515     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2516     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2517     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2518     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2519     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2520     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2521     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2522     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2523     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2524     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2525     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2526     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2527     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2528     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2529     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2530     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2531     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2532     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2533     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2534     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2535     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2536 \r
2537     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2538       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2539       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2540       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2541       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2542       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2543       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2545       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2546       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2547       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2548       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2549       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2550     } else {\r
2551       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2552       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2553       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2554       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2555       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2556       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2557       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2558       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2559       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2560       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2561       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2562       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2563     }\r
2564 \r
2565   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2566     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2567     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2568     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2569     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2570     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2571     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2572     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2573     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2574     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2575     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2576     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2577     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2578     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2579     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2580   }\r
2581 \r
2582 \r
2583   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2584   /* special Shogi support in this size */\r
2585   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2586       for (piece = WhitePawn;\r
2587            (int) piece < (int) BlackPawn;\r
2588            piece = (ChessSquare) ((int) piece + 1)) {\r
2589         if (pieceBitmap[i][piece] != NULL)\r
2590           DeleteObject(pieceBitmap[i][piece]);\r
2591       }\r
2592     }\r
2593   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2594   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2595   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2596   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2597   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2598   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2599   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2600   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2601   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2602   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2603   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2604   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2605   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2606   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2607   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2608   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2609   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2610   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2611   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2612   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2613   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2614   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2615   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2616   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2617   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2618   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2619   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2620   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2621   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2622   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2623   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2624   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2625   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2627   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2628   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2629   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2630   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2631   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2632   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2633   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2634   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2635   minorSize = 0;\r
2636   }\r
2637 }\r
2638 \r
2639 HBITMAP\r
2640 PieceBitmap(ChessSquare p, int kind)\r
2641 {\r
2642   if ((int) p >= (int) BlackPawn)\r
2643     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2644 \r
2645   return pieceBitmap[kind][(int) p];\r
2646 }\r
2647 \r
2648 /***************************************************************/\r
2649 \r
2650 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2651 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2652 /*\r
2653 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2654 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2655 */\r
2656 \r
2657 VOID\r
2658 SquareToPos(int row, int column, int * x, int * y)\r
2659 {\r
2660   if (flipView) {\r
2661     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2662     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2663   } else {\r
2664     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2665     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2666   }\r
2667 }\r
2668 \r
2669 VOID\r
2670 DrawCoordsOnDC(HDC hdc)\r
2671 {\r
2672   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
2673   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
2674   char str[2] = { NULLCHAR, NULLCHAR };\r
2675   int oldMode, oldAlign, x, y, start, i;\r
2676   HFONT oldFont;\r
2677   HBRUSH oldBrush;\r
2678 \r
2679   if (!appData.showCoords)\r
2680     return;\r
2681 \r
2682   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2683 \r
2684   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2685   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2686   oldAlign = GetTextAlign(hdc);\r
2687   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2688 \r
2689   y = boardRect.top + lineGap;\r
2690   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2691 \r
2692   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2693   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2694     str[0] = files[start + i];\r
2695     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2696     y += squareSize + lineGap;\r
2697   }\r
2698 \r
2699   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2700 \r
2701   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2702   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2703     str[0] = ranks[start + i];\r
2704     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2705     x += squareSize + lineGap;\r
2706   }    \r
2707 \r
2708   SelectObject(hdc, oldBrush);\r
2709   SetBkMode(hdc, oldMode);\r
2710   SetTextAlign(hdc, oldAlign);\r
2711   SelectObject(hdc, oldFont);\r
2712 }\r
2713 \r
2714 VOID\r
2715 DrawGridOnDC(HDC hdc)\r
2716 {\r
2717   HPEN oldPen;\r
2718  \r
2719   if (lineGap != 0) {\r
2720     oldPen = SelectObject(hdc, gridPen);\r
2721     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2722     SelectObject(hdc, oldPen);\r
2723   }\r
2724 }\r
2725 \r
2726 #define HIGHLIGHT_PEN 0\r
2727 #define PREMOVE_PEN   1\r
2728 \r
2729 VOID\r
2730 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2731 {\r
2732   int x1, y1;\r
2733   HPEN oldPen, hPen;\r
2734   if (lineGap == 0) return;\r
2735   if (flipView) {\r
2736     x1 = boardRect.left +\r
2737       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2738     y1 = boardRect.top +\r
2739       lineGap/2 + y * (squareSize + lineGap);\r
2740   } else {\r
2741     x1 = boardRect.left +\r
2742       lineGap/2 + x * (squareSize + lineGap);\r
2743     y1 = boardRect.top +\r
2744       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2745   }\r
2746   hPen = pen ? premovePen : highlightPen;\r
2747   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2748   MoveToEx(hdc, x1, y1, NULL);\r
2749   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2750   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2751   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2752   LineTo(hdc, x1, y1);\r
2753   SelectObject(hdc, oldPen);\r
2754 }\r
2755 \r
2756 VOID\r
2757 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2758 {\r
2759   int i;\r
2760   for (i=0; i<2; i++) {\r
2761     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2762       DrawHighlightOnDC(hdc, TRUE,\r
2763                         h->sq[i].x, h->sq[i].y,\r
2764                         pen);\r
2765   }\r
2766 }\r
2767 \r
2768 /* Note: sqcolor is used only in monoMode */\r
2769 /* Note that this code is largely duplicated in woptions.c,\r
2770    function DrawSampleSquare, so that needs to be updated too */\r
2771 VOID\r
2772 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2773 {\r
2774   HBITMAP oldBitmap;\r
2775   HBRUSH oldBrush;\r
2776   int tmpSize;\r
2777 \r
2778   if (appData.blindfold) return;\r
2779 \r
2780   /* [AS] Use font-based pieces if needed */\r
2781   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2782     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2783     CreatePiecesFromFont();\r
2784 \r
2785     if( fontBitmapSquareSize == squareSize ) {\r
2786         int index = TranslatePieceToFontPiece(piece);\r
2787 \r
2788         SelectObject( tmphdc, hPieceMask[ index ] );\r
2789 \r
2790       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2791         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2792       else\r
2793         BitBlt( hdc,\r
2794             x, y,\r
2795             squareSize, squareSize,\r
2796             tmphdc,\r
2797             0, 0,\r
2798             SRCAND );\r
2799 \r
2800         SelectObject( tmphdc, hPieceFace[ index ] );\r
2801 \r
2802       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2803         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2804       else\r
2805         BitBlt( hdc,\r
2806             x, y,\r
2807             squareSize, squareSize,\r
2808             tmphdc,\r
2809             0, 0,\r
2810             SRCPAINT );\r
2811 \r
2812         return;\r
2813     }\r
2814   }\r
2815 \r
2816   if (appData.monoMode) {\r
2817     SelectObject(tmphdc, PieceBitmap(piece, \r
2818       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2819     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2820            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2821   } else {\r
2822     tmpSize = squareSize;\r
2823     if(minorSize &&\r
2824         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2825          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2826       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2827       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2828       x += (squareSize - minorSize)>>1;\r
2829       y += squareSize - minorSize - 2;\r
2830       tmpSize = minorSize;\r
2831     }\r
2832     if (color || appData.allWhite ) {\r
2833       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2834       if( color )\r
2835               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2836       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2837       if(appData.upsideDown && color==flipView)\r
2838         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2839       else\r
2840         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2841       /* Use black for outline of white pieces */\r
2842       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2843       if(appData.upsideDown && color==flipView)\r
2844         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2845       else\r
2846         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2847     } else {\r
2848       /* Use square color for details of black pieces */\r
2849       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2850       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2851       if(appData.upsideDown && !flipView)\r
2852         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2853       else\r
2854         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2855     }\r
2856     SelectObject(hdc, oldBrush);\r
2857     SelectObject(tmphdc, oldBitmap);\r
2858   }\r
2859 }\r
2860 \r
2861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2862 int GetBackTextureMode( int algo )\r
2863 {\r
2864     int result = BACK_TEXTURE_MODE_DISABLED;\r
2865 \r
2866     switch( algo ) \r
2867     {\r
2868         case BACK_TEXTURE_MODE_PLAIN:\r
2869             result = 1; /* Always use identity map */\r
2870             break;\r
2871         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2872             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2873             break;\r
2874     }\r
2875 \r
2876     return result;\r
2877 }\r
2878 \r
2879 /* \r
2880     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2881     to handle redraws cleanly (as random numbers would always be different).\r
2882 */\r
2883 VOID RebuildTextureSquareInfo()\r
2884 {\r
2885     BITMAP bi;\r
2886     int lite_w = 0;\r
2887     int lite_h = 0;\r
2888     int dark_w = 0;\r
2889     int dark_h = 0;\r
2890     int row;\r
2891     int col;\r
2892 \r
2893     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2894 \r
2895     if( liteBackTexture != NULL ) {\r
2896         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2897             lite_w = bi.bmWidth;\r
2898             lite_h = bi.bmHeight;\r
2899         }\r
2900     }\r
2901 \r
2902     if( darkBackTexture != NULL ) {\r
2903         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2904             dark_w = bi.bmWidth;\r
2905             dark_h = bi.bmHeight;\r
2906         }\r
2907     }\r
2908 \r
2909     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2910         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2911             if( (col + row) & 1 ) {\r
2912                 /* Lite square */\r
2913                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2914                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2915                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2916                   else\r
2917                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2918                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2919                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2920                   else\r
2921                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2922                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2923                 }\r
2924             }\r
2925             else {\r
2926                 /* Dark square */\r
2927                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2928                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2929                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2932                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2933                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2934                   else\r
2935                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2936                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2937                 }\r
2938             }\r
2939         }\r
2940     }\r
2941 }\r
2942 \r
2943 /* [AS] Arrow highlighting support */\r
2944 \r
2945 static int A_WIDTH = 5; /* Width of arrow body */\r
2946 \r
2947 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2948 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2949 \r
2950 static double Sqr( double x )\r
2951 {\r
2952     return x*x;\r
2953 }\r
2954 \r
2955 static int Round( double x )\r
2956 {\r
2957     return (int) (x + 0.5);\r
2958 }\r
2959 \r
2960 /* Draw an arrow between two points using current settings */\r
2961 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2962 {\r
2963     POINT arrow[7];\r
2964     double dx, dy, j, k, x, y;\r
2965 \r
2966     if( d_x == s_x ) {\r
2967         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2968 \r
2969         arrow[0].x = s_x + A_WIDTH;\r
2970         arrow[0].y = s_y;\r
2971 \r
2972         arrow[1].x = s_x + A_WIDTH;\r
2973         arrow[1].y = d_y - h;\r
2974 \r
2975         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2976         arrow[2].y = d_y - h;\r
2977 \r
2978         arrow[3].x = d_x;\r
2979         arrow[3].y = d_y;\r
2980 \r
2981         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2982         arrow[4].y = d_y - h;\r
2983 \r
2984         arrow[5].x = s_x - A_WIDTH;\r
2985         arrow[5].y = d_y - h;\r
2986 \r
2987         arrow[6].x = s_x - A_WIDTH;\r
2988         arrow[6].y = s_y;\r
2989     }\r
2990     else if( d_y == s_y ) {\r
2991         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2992 \r
2993         arrow[0].x = s_x;\r
2994         arrow[0].y = s_y + A_WIDTH;\r
2995 \r
2996         arrow[1].x = d_x - w;\r
2997         arrow[1].y = s_y + A_WIDTH;\r
2998 \r
2999         arrow[2].x = d_x - w;\r
3000         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3001 \r
3002         arrow[3].x = d_x;\r
3003         arrow[3].y = d_y;\r
3004 \r
3005         arrow[4].x = d_x - w;\r
3006         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3007 \r
3008         arrow[5].x = d_x - w;\r
3009         arrow[5].y = s_y - A_WIDTH;\r
3010 \r
3011         arrow[6].x = s_x;\r
3012         arrow[6].y = s_y - A_WIDTH;\r
3013     }\r
3014     else {\r
3015         /* [AS] Needed a lot of paper for this! :-) */\r
3016         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3017         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3018   \r
3019         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3020 \r
3021         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3022 \r
3023         x = s_x;\r
3024         y = s_y;\r
3025 \r
3026         arrow[0].x = Round(x - j);\r
3027         arrow[0].y = Round(y + j*dx);\r
3028 \r
3029         arrow[1].x = Round(x + j);\r
3030         arrow[1].y = Round(y - j*dx);\r
3031 \r
3032         if( d_x > s_x ) {\r
3033             x = (double) d_x - k;\r
3034             y = (double) d_y - k*dy;\r
3035         }\r
3036         else {\r
3037             x = (double) d_x + k;\r
3038             y = (double) d_y + k*dy;\r
3039         }\r
3040 \r
3041         arrow[2].x = Round(x + j);\r
3042         arrow[2].y = Round(y - j*dx);\r
3043 \r
3044         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3045         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3046 \r
3047         arrow[4].x = d_x;\r
3048         arrow[4].y = d_y;\r
3049 \r
3050         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3051         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3052 \r
3053         arrow[6].x = Round(x - j);\r
3054         arrow[6].y = Round(y + j*dx);\r
3055     }\r
3056 \r
3057     Polygon( hdc, arrow, 7 );\r
3058 }\r
3059 \r
3060 /* [AS] Draw an arrow between two squares */\r
3061 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3062 {\r
3063     int s_x, s_y, d_x, d_y;\r
3064     HPEN hpen;\r
3065     HPEN holdpen;\r
3066     HBRUSH hbrush;\r
3067     HBRUSH holdbrush;\r
3068     LOGBRUSH stLB;\r
3069 \r
3070     if( s_col == d_col && s_row == d_row ) {\r
3071         return;\r
3072     }\r
3073 \r
3074     /* Get source and destination points */\r
3075     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3076     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3077 \r
3078     if( d_y > s_y ) {\r
3079         d_y += squareSize / 4;\r
3080     }\r
3081     else if( d_y < s_y ) {\r
3082         d_y += 3 * squareSize / 4;\r
3083     }\r
3084     else {\r
3085         d_y += squareSize / 2;\r
3086     }\r
3087 \r
3088     if( d_x > s_x ) {\r
3089         d_x += squareSize / 4;\r
3090     }\r
3091     else if( d_x < s_x ) {\r
3092         d_x += 3 * squareSize / 4;\r
3093     }\r
3094     else {\r
3095         d_x += squareSize / 2;\r
3096     }\r
3097 \r
3098     s_x += squareSize / 2;\r
3099     s_y += squareSize / 2;\r
3100 \r
3101     /* Adjust width */\r
3102     A_WIDTH = squareSize / 14;\r
3103 \r
3104     /* Draw */\r
3105     stLB.lbStyle = BS_SOLID;\r
3106     stLB.lbColor = appData.highlightArrowColor;\r
3107     stLB.lbHatch = 0;\r
3108 \r
3109     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3110     holdpen = SelectObject( hdc, hpen );\r
3111     hbrush = CreateBrushIndirect( &stLB );\r
3112     holdbrush = SelectObject( hdc, hbrush );\r
3113 \r
3114     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3115 \r
3116     SelectObject( hdc, holdpen );\r
3117     SelectObject( hdc, holdbrush );\r
3118     DeleteObject( hpen );\r
3119     DeleteObject( hbrush );\r
3120 }\r
3121 \r
3122 BOOL HasHighlightInfo()\r
3123 {\r
3124     BOOL result = FALSE;\r
3125 \r
3126     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3127         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3128     {\r
3129         result = TRUE;\r
3130     }\r
3131 \r
3132     return result;\r
3133 }\r
3134 \r
3135 BOOL IsDrawArrowEnabled()\r
3136 {\r
3137     BOOL result = FALSE;\r
3138 \r
3139     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3140         result = TRUE;\r
3141     }\r
3142 \r
3143     return result;\r
3144 }\r
3145 \r
3146 VOID DrawArrowHighlight( HDC hdc )\r
3147 {\r
3148     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3149         DrawArrowBetweenSquares( hdc,\r
3150             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3151             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3152     }\r
3153 }\r
3154 \r
3155 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3156 {\r
3157     HRGN result = NULL;\r
3158 \r
3159     if( HasHighlightInfo() ) {\r
3160         int x1, y1, x2, y2;\r
3161         int sx, sy, dx, dy;\r
3162 \r
3163         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3164         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3165 \r
3166         sx = MIN( x1, x2 );\r
3167         sy = MIN( y1, y2 );\r
3168         dx = MAX( x1, x2 ) + squareSize;\r
3169         dy = MAX( y1, y2 ) + squareSize;\r
3170 \r
3171         result = CreateRectRgn( sx, sy, dx, dy );\r
3172     }\r
3173 \r
3174     return result;\r
3175 }\r
3176 \r
3177 /*\r
3178     Warning: this function modifies the behavior of several other functions. \r
3179     \r
3180     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3181     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3182     repaint is scattered all over the place, which is not good for features such as\r
3183     "arrow highlighting" that require a full repaint of the board.\r
3184 \r
3185     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3186     user interaction, when speed is not so important) but especially to avoid errors\r
3187     in the displayed graphics.\r
3188 \r
3189     In such patched places, I always try refer to this function so there is a single\r
3190     place to maintain knowledge.\r
3191     \r
3192     To restore the original behavior, just return FALSE unconditionally.\r
3193 */\r
3194 BOOL IsFullRepaintPreferrable()\r
3195 {\r
3196     BOOL result = FALSE;\r
3197 \r
3198     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3199         /* Arrow may appear on the board */\r
3200         result = TRUE;\r
3201     }\r
3202 \r
3203     return result;\r
3204 }\r
3205 \r
3206 /* \r
3207     This function is called by DrawPosition to know whether a full repaint must\r
3208     be forced or not.\r
3209 \r
3210     Only DrawPosition may directly call this function, which makes use of \r
3211     some state information. Other function should call DrawPosition specifying \r
3212     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3213 */\r
3214 BOOL DrawPositionNeedsFullRepaint()\r
3215 {\r
3216     BOOL result = FALSE;\r
3217 \r
3218     /* \r
3219         Probably a slightly better policy would be to trigger a full repaint\r
3220         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3221         but animation is fast enough that it's difficult to notice.\r
3222     */\r
3223     if( animInfo.piece == EmptySquare ) {\r
3224         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3225             result = TRUE;\r
3226         }\r
3227     }\r
3228 \r
3229     return result;\r
3230 }\r
3231 \r
3232 VOID\r
3233 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3234 {\r
3235   int row, column, x, y, square_color, piece_color;\r
3236   ChessSquare piece;\r
3237   HBRUSH oldBrush;\r
3238   HDC texture_hdc = NULL;\r
3239 \r
3240   /* [AS] Initialize background textures if needed */\r
3241   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3242       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3243       if( backTextureSquareSize != squareSize \r
3244        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3245           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3246           backTextureSquareSize = squareSize;\r
3247           RebuildTextureSquareInfo();\r
3248       }\r
3249 \r
3250       texture_hdc = CreateCompatibleDC( hdc );\r
3251   }\r
3252 \r
3253   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3254     for (column = 0; column < BOARD_WIDTH; column++) {\r
3255   \r
3256       SquareToPos(row, column, &x, &y);\r
3257 \r
3258       piece = board[row][column];\r
3259 \r
3260       square_color = ((column + row) % 2) == 1;\r
3261       if( gameInfo.variant == VariantXiangqi ) {\r
3262           square_color = !InPalace(row, column);\r
3263           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3264           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3265       }\r
3266       piece_color = (int) piece < (int) BlackPawn;\r
3267 \r
3268 \r
3269       /* [HGM] holdings file: light square or black */\r
3270       if(column == BOARD_LEFT-2) {\r
3271             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3272                 square_color = 1;\r
3273             else {\r
3274                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3275                 continue;\r
3276             }\r
3277       } else\r
3278       if(column == BOARD_RGHT + 1 ) {\r
3279             if( row < gameInfo.holdingsSize )\r
3280                 square_color = 1;\r
3281             else {\r
3282                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3283                 continue;\r
3284             }\r
3285       }\r
3286       if(column == BOARD_LEFT-1 ) /* left align */\r
3287             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3288       else if( column == BOARD_RGHT) /* right align */\r
3289             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3290       else\r
3291       if (appData.monoMode) {\r
3292         if (piece == EmptySquare) {\r
3293           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3294                  square_color ? WHITENESS : BLACKNESS);\r
3295         } else {\r
3296           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3297         }\r
3298       } \r
3299       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3300           /* [AS] Draw the square using a texture bitmap */\r
3301           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3302           int r = row, c = column; // [HGM] do not flip board in flipView\r
3303           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3304 \r
3305           DrawTile( x, y, \r
3306               squareSize, squareSize, \r
3307               hdc, \r
3308               texture_hdc,\r
3309               backTextureSquareInfo[r][c].mode,\r
3310               backTextureSquareInfo[r][c].x,\r
3311               backTextureSquareInfo[r][c].y );\r
3312 \r
3313           SelectObject( texture_hdc, hbm );\r
3314 \r
3315           if (piece != EmptySquare) {\r
3316               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3317           }\r
3318       }\r
3319       else {\r
3320         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3321 \r
3322         oldBrush = SelectObject(hdc, brush );\r
3323         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3324         SelectObject(hdc, oldBrush);\r
3325         if (piece != EmptySquare)\r
3326           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3327       }\r
3328     }\r
3329   }\r
3330 \r
3331   if( texture_hdc != NULL ) {\r
3332     DeleteDC( texture_hdc );\r
3333   }\r
3334 }\r
3335 \r
3336 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3337 void fputDW(FILE *f, int x)\r
3338 {\r
3339         fputc(x     & 255, f);\r
3340         fputc(x>>8  & 255, f);\r
3341         fputc(x>>16 & 255, f);\r
3342         fputc(x>>24 & 255, f);\r
3343 }\r
3344 \r
3345 #define MAX_CLIPS 200   /* more than enough */\r
3346 \r
3347 VOID\r
3348 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3349 {\r
3350 //  HBITMAP bufferBitmap;\r
3351   BITMAP bi;\r
3352 //  RECT Rect;\r
3353   HDC tmphdc;\r
3354   HBITMAP hbm;\r
3355   int w = 100, h = 50;\r
3356 \r
3357   if(logo == NULL) return;\r
3358 //  GetClientRect(hwndMain, &Rect);\r
3359 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3360 //                                      Rect.bottom-Rect.top+1);\r
3361   tmphdc = CreateCompatibleDC(hdc);\r
3362   hbm = SelectObject(tmphdc, logo);\r
3363   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3364             w = bi.bmWidth;\r
3365             h = bi.bmHeight;\r
3366   }\r
3367   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3368                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3369   SelectObject(tmphdc, hbm);\r
3370   DeleteDC(tmphdc);\r
3371 }\r
3372 \r
3373 static HDC hdcSeek;\r
3374 \r
3375 // [HGM] seekgraph\r
3376 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3377 {\r
3378     POINT stPt;\r
3379     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3380     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3381     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3382     SelectObject( hdcSeek, hp );\r
3383 }\r
3384 \r
3385 // front-end wrapper for drawing functions to do rectangles\r
3386 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3387 {\r
3388     HPEN hp;\r
3389     RECT rc;\r
3390 \r
3391     if (hdcSeek == NULL) {\r
3392     hdcSeek = GetDC(hwndMain);\r
3393       if (!appData.monoMode) {\r
3394         SelectPalette(hdcSeek, hPal, FALSE);\r
3395         RealizePalette(hdcSeek);\r
3396       }\r
3397     }\r
3398     hp = SelectObject( hdcSeek, gridPen );\r
3399     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3400     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3401     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3402     SelectObject( hdcSeek, hp );\r
3403 }\r
3404 \r
3405 // front-end wrapper for putting text in graph\r
3406 void DrawSeekText(char *buf, int x, int y)\r
3407 {\r
3408         SIZE stSize;\r
3409         SetBkMode( hdcSeek, TRANSPARENT );\r
3410         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3411         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3412 }\r
3413 \r
3414 void DrawSeekDot(int x, int y, int color)\r
3415 {\r
3416         int square = color & 0x80;\r
3417         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3418                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3419         color &= 0x7F;\r
3420         if(square)\r
3421             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3422                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3423         else\r
3424             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3425                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3426             SelectObject(hdcSeek, oldBrush);\r
3427 }\r
3428 \r
3429 VOID\r
3430 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3431 {\r
3432   static Board lastReq[2], lastDrawn[2];\r
3433   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3434   static int lastDrawnFlipView = 0;\r
3435   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3436   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3437   HDC tmphdc;\r
3438   HDC hdcmem;\r
3439   HBITMAP bufferBitmap;\r
3440   HBITMAP oldBitmap;\r
3441   RECT Rect;\r
3442   HRGN clips[MAX_CLIPS];\r
3443   ChessSquare dragged_piece = EmptySquare;\r
3444   int nr = twoBoards*partnerUp;\r
3445 \r
3446   /* I'm undecided on this - this function figures out whether a full\r
3447    * repaint is necessary on its own, so there's no real reason to have the\r
3448    * caller tell it that.  I think this can safely be set to FALSE - but\r
3449    * if we trust the callers not to request full repaints unnessesarily, then\r
3450    * we could skip some clipping work.  In other words, only request a full\r
3451    * redraw when the majority of pieces have changed positions (ie. flip, \r
3452    * gamestart and similar)  --Hawk\r
3453    */\r
3454   Boolean fullrepaint = repaint;\r
3455 \r
3456   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3457 \r
3458   if( DrawPositionNeedsFullRepaint() ) {\r
3459       fullrepaint = TRUE;\r
3460   }\r
3461 \r
3462   if (board == NULL) {\r
3463     if (!lastReqValid[nr]) {\r
3464       return;\r
3465     }\r
3466     board = lastReq[nr];\r
3467   } else {\r
3468     CopyBoard(lastReq[nr], board);\r
3469     lastReqValid[nr] = 1;\r
3470   }\r
3471 \r
3472   if (doingSizing) {\r
3473     return;\r
3474   }\r
3475 \r
3476   if (IsIconic(hwndMain)) {\r
3477     return;\r
3478   }\r
3479 \r
3480   if (hdc == NULL) {\r
3481     hdc = GetDC(hwndMain);\r
3482     if (!appData.monoMode) {\r
3483       SelectPalette(hdc, hPal, FALSE);\r
3484       RealizePalette(hdc);\r
3485     }\r
3486     releaseDC = TRUE;\r
3487   } else {\r
3488     releaseDC = FALSE;\r
3489   }\r
3490 \r
3491   /* Create some work-DCs */\r
3492   hdcmem = CreateCompatibleDC(hdc);\r
3493   tmphdc = CreateCompatibleDC(hdc);\r
3494 \r
3495   /* If dragging is in progress, we temporarely remove the piece */\r
3496   /* [HGM] or temporarily decrease count if stacked              */\r
3497   /*       !! Moved to before board compare !!                   */\r
3498   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3499     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3500     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3501             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3502         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3503     } else \r
3504     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3505             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3506         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3507     } else \r
3508         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3509   }\r
3510 \r
3511   /* Figure out which squares need updating by comparing the \r
3512    * newest board with the last drawn board and checking if\r
3513    * flipping has changed.\r
3514    */\r
3515   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3516     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3517       for (column = 0; column < BOARD_WIDTH; column++) {\r
3518         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3519           SquareToPos(row, column, &x, &y);\r
3520           clips[num_clips++] =\r
3521             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3522         }\r
3523       }\r
3524     }\r
3525    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3526     for (i=0; i<2; i++) {\r
3527       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3528           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3529         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3530             lastDrawnHighlight.sq[i].y >= 0) {\r
3531           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3532                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3533           clips[num_clips++] =\r
3534             CreateRectRgn(x - lineGap, y - lineGap, \r
3535                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3536         }\r
3537         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3538           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3539           clips[num_clips++] =\r
3540             CreateRectRgn(x - lineGap, y - lineGap, \r
3541                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3542         }\r
3543       }\r
3544     }\r
3545     for (i=0; i<2; i++) {\r
3546       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3547           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3548         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3549             lastDrawnPremove.sq[i].y >= 0) {\r
3550           SquareToPos(lastDrawnPremove.sq[i].y,\r
3551                       lastDrawnPremove.sq[i].x, &x, &y);\r
3552           clips[num_clips++] =\r
3553             CreateRectRgn(x - lineGap, y - lineGap, \r
3554                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3555         }\r
3556         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3557             premoveHighlightInfo.sq[i].y >= 0) {\r
3558           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3559                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3560           clips[num_clips++] =\r
3561             CreateRectRgn(x - lineGap, y - lineGap, \r
3562                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3563         }\r
3564       }\r
3565     }\r
3566    } else { // nr == 1\r
3567         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3568         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3569         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3570         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3571       for (i=0; i<2; i++) {\r
3572         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3573             partnerHighlightInfo.sq[i].y >= 0) {\r
3574           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3575                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3576           clips[num_clips++] =\r
3577             CreateRectRgn(x - lineGap, y - lineGap, \r
3578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3579         }\r
3580         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3581             oldPartnerHighlight.sq[i].y >= 0) {\r
3582           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3583                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3584           clips[num_clips++] =\r
3585             CreateRectRgn(x - lineGap, y - lineGap, \r
3586                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3587         }\r
3588       }\r
3589    }\r
3590   } else {\r
3591     fullrepaint = TRUE;\r
3592   }\r
3593 \r
3594   /* Create a buffer bitmap - this is the actual bitmap\r
3595    * being written to.  When all the work is done, we can\r
3596    * copy it to the real DC (the screen).  This avoids\r
3597    * the problems with flickering.\r
3598    */\r
3599   GetClientRect(hwndMain, &Rect);\r
3600   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3601                                         Rect.bottom-Rect.top+1);\r
3602   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3603   if (!appData.monoMode) {\r
3604     SelectPalette(hdcmem, hPal, FALSE);\r
3605   }\r
3606 \r
3607   /* Create clips for dragging */\r
3608   if (!fullrepaint) {\r
3609     if (dragInfo.from.x >= 0) {\r
3610       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3611       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3612     }\r
3613     if (dragInfo.start.x >= 0) {\r
3614       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3615       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3616     }\r
3617     if (dragInfo.pos.x >= 0) {\r
3618       x = dragInfo.pos.x - squareSize / 2;\r
3619       y = dragInfo.pos.y - squareSize / 2;\r
3620       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3621     }\r
3622     if (dragInfo.lastpos.x >= 0) {\r
3623       x = dragInfo.lastpos.x - squareSize / 2;\r
3624       y = dragInfo.lastpos.y - squareSize / 2;\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627   }\r
3628 \r
3629   /* Are we animating a move?  \r
3630    * If so, \r
3631    *   - remove the piece from the board (temporarely)\r
3632    *   - calculate the clipping region\r
3633    */\r
3634   if (!fullrepaint) {\r
3635     if (animInfo.piece != EmptySquare) {\r
3636       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3637       x = boardRect.left + animInfo.lastpos.x;\r
3638       y = boardRect.top + animInfo.lastpos.y;\r
3639       x2 = boardRect.left + animInfo.pos.x;\r
3640       y2 = boardRect.top + animInfo.pos.y;\r
3641       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3642       /* Slight kludge.  The real problem is that after AnimateMove is\r
3643          done, the position on the screen does not match lastDrawn.\r
3644          This currently causes trouble only on e.p. captures in\r
3645          atomic, where the piece moves to an empty square and then\r
3646          explodes.  The old and new positions both had an empty square\r
3647          at the destination, but animation has drawn a piece there and\r
3648          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3649       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3650     }\r
3651   }\r
3652 \r
3653   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3654   if (num_clips == 0)\r
3655     fullrepaint = TRUE;\r
3656 \r
3657   /* Set clipping on the memory DC */\r
3658   if (!fullrepaint) {\r
3659     SelectClipRgn(hdcmem, clips[0]);\r
3660     for (x = 1; x < num_clips; x++) {\r
3661       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3662         abort();  // this should never ever happen!\r
3663     }\r
3664   }\r
3665 \r
3666   /* Do all the drawing to the memory DC */\r
3667   if(explodeInfo.radius) { // [HGM] atomic\r
3668         HBRUSH oldBrush;\r
3669         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3670         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3671         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3672         x += squareSize/2;\r
3673         y += squareSize/2;\r
3674         if(!fullrepaint) {\r
3675           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3676           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3677         }\r
3678         DrawGridOnDC(hdcmem);\r
3679         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3680         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3681         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3682         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3683         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3684         SelectObject(hdcmem, oldBrush);\r
3685   } else {\r
3686     DrawGridOnDC(hdcmem);\r
3687     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3688         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3689         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3690     } else {\r
3691         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3692         oldPartnerHighlight = partnerHighlightInfo;\r
3693     }\r
3694     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3695   }\r
3696   if(nr == 0) // [HGM] dual: markers only on left board\r
3697   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3698     for (column = 0; column < BOARD_WIDTH; column++) {\r
3699         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3700             HBRUSH oldBrush = SelectObject(hdcmem, \r
3701                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3702             SquareToPos(row, column, &x, &y);\r
3703             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3704                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3705             SelectObject(hdcmem, oldBrush);\r
3706         }\r
3707     }\r
3708   }\r
3709   if(logoHeight) {\r
3710         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3711         if(appData.autoLogo) {\r
3712           \r
3713           switch(gameMode) { // pick logos based on game mode\r
3714             case IcsObserving:\r
3715                 whiteLogo = second.programLogo; // ICS logo\r
3716                 blackLogo = second.programLogo;\r
3717             default:\r
3718                 break;\r
3719             case IcsPlayingWhite:\r
3720                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3721                 blackLogo = second.programLogo; // ICS logo\r
3722                 break;\r
3723             case IcsPlayingBlack:\r
3724                 whiteLogo = second.programLogo; // ICS logo\r
3725                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3726                 break;\r
3727             case TwoMachinesPlay:\r
3728                 if(first.twoMachinesColor[0] == 'b') {\r
3729                     whiteLogo = second.programLogo;\r
3730                     blackLogo = first.programLogo;\r
3731                 }\r
3732                 break;\r
3733             case MachinePlaysWhite:\r
3734                 blackLogo = userLogo;\r
3735                 break;\r
3736             case MachinePlaysBlack:\r
3737                 whiteLogo = userLogo;\r
3738                 blackLogo = first.programLogo;\r
3739           }\r
3740         }\r
3741         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3742         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3743   }\r
3744 \r
3745   if( appData.highlightMoveWithArrow ) {\r
3746     DrawArrowHighlight(hdcmem);\r
3747   }\r
3748 \r
3749   DrawCoordsOnDC(hdcmem);\r
3750 \r
3751   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3752                  /* to make sure lastDrawn contains what is actually drawn */\r
3753 \r
3754   /* Put the dragged piece back into place and draw it (out of place!) */\r
3755     if (dragged_piece != EmptySquare) {\r
3756     /* [HGM] or restack */\r
3757     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3758                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3759     else\r
3760     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3761                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3762     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3763     x = dragInfo.pos.x - squareSize / 2;\r
3764     y = dragInfo.pos.y - squareSize / 2;\r
3765     DrawPieceOnDC(hdcmem, dragged_piece,\r
3766                   ((int) dragged_piece < (int) BlackPawn), \r
3767                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3768   }   \r
3769   \r
3770   /* Put the animated piece back into place and draw it */\r
3771   if (animInfo.piece != EmptySquare) {\r
3772     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3773     x = boardRect.left + animInfo.pos.x;\r
3774     y = boardRect.top + animInfo.pos.y;\r
3775     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3776                   ((int) animInfo.piece < (int) BlackPawn),\r
3777                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3778   }\r
3779 \r
3780   /* Release the bufferBitmap by selecting in the old bitmap \r
3781    * and delete the memory DC\r
3782    */\r
3783   SelectObject(hdcmem, oldBitmap);\r
3784   DeleteDC(hdcmem);\r
3785 \r
3786   /* Set clipping on the target DC */\r
3787   if (!fullrepaint) {\r
3788     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3789         RECT rect;\r
3790         GetRgnBox(clips[x], &rect);\r
3791         DeleteObject(clips[x]);\r
3792         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3793                           rect.right + wpMain.width/2, rect.bottom);\r
3794     }\r
3795     SelectClipRgn(hdc, clips[0]);\r
3796     for (x = 1; x < num_clips; x++) {\r
3797       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3798         abort();   // this should never ever happen!\r
3799     } \r
3800   }\r
3801 \r
3802   /* Copy the new bitmap onto the screen in one go.\r
3803    * This way we avoid any flickering\r
3804    */\r
3805   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3806   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3807          boardRect.right - boardRect.left,\r
3808          boardRect.bottom - boardRect.top,\r
3809          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3810   if(saveDiagFlag) { \r
3811     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3812     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3813 \r
3814     GetObject(bufferBitmap, sizeof(b), &b);\r
3815     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3816         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3817         bih.biWidth = b.bmWidth;\r
3818         bih.biHeight = b.bmHeight;\r
3819         bih.biPlanes = 1;\r
3820         bih.biBitCount = b.bmBitsPixel;\r
3821         bih.biCompression = 0;\r
3822         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3823         bih.biXPelsPerMeter = 0;\r
3824         bih.biYPelsPerMeter = 0;\r
3825         bih.biClrUsed = 0;\r
3826         bih.biClrImportant = 0;\r
3827 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3828 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3829         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3830 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3831 \r
3832         wb = b.bmWidthBytes;\r
3833         // count colors\r
3834         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3835                 int k = ((int*) pData)[i];\r
3836                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3837                 if(j >= 16) break;\r
3838                 color[j] = k;\r
3839                 if(j >= nrColors) nrColors = j+1;\r
3840         }\r
3841         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3842                 INT p = 0;\r
3843                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3844                     for(w=0; w<(wb>>2); w+=2) {\r
3845                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3846                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3847                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3848                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3849                         pData[p++] = m | j<<4;\r
3850                     }\r
3851                     while(p&3) pData[p++] = 0;\r
3852                 }\r
3853                 fac = 3;\r
3854                 wb = ((wb+31)>>5)<<2;\r
3855         }\r
3856         // write BITMAPFILEHEADER\r
3857         fprintf(diagFile, "BM");\r
3858         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3859         fputDW(diagFile, 0);\r
3860         fputDW(diagFile, 0x36 + (fac?64:0));\r
3861         // write BITMAPINFOHEADER\r
3862         fputDW(diagFile, 40);\r
3863         fputDW(diagFile, b.bmWidth);\r
3864         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3865         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3866         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3867         fputDW(diagFile, 0);\r
3868         fputDW(diagFile, 0);\r
3869         fputDW(diagFile, 0);\r
3870         fputDW(diagFile, 0);\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0);\r
3873         // write color table\r
3874         if(fac)\r
3875         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3876         // write bitmap data\r
3877         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3878                 fputc(pData[i], diagFile);\r
3879      }\r
3880   }\r
3881 \r
3882   SelectObject(tmphdc, oldBitmap);\r
3883 \r
3884   /* Massive cleanup */\r
3885   for (x = 0; x < num_clips; x++)\r
3886     DeleteObject(clips[x]);\r
3887 \r
3888   DeleteDC(tmphdc);\r
3889   DeleteObject(bufferBitmap);\r
3890 \r
3891   if (releaseDC) \r
3892     ReleaseDC(hwndMain, hdc);\r
3893   \r
3894   if (lastDrawnFlipView != flipView && nr == 0) {\r
3895     if (flipView)\r
3896       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3897     else\r
3898       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3899   }\r
3900 \r
3901 /*  CopyBoard(lastDrawn, board);*/\r
3902   lastDrawnHighlight = highlightInfo;\r
3903   lastDrawnPremove   = premoveHighlightInfo;\r
3904   lastDrawnFlipView = flipView;\r
3905   lastDrawnValid[nr] = 1;\r
3906 }\r
3907 \r
3908 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3909 int\r
3910 SaveDiagram(f)\r
3911      FILE *f;\r
3912 {\r
3913     saveDiagFlag = 1; diagFile = f;\r
3914     HDCDrawPosition(NULL, TRUE, NULL);\r
3915 \r
3916     saveDiagFlag = 0;\r
3917 \r
3918 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3919     \r
3920     fclose(f);\r
3921     return TRUE;\r
3922 }\r
3923 \r
3924 \r
3925 /*---------------------------------------------------------------------------*\\r
3926 | CLIENT PAINT PROCEDURE\r
3927 |   This is the main event-handler for the WM_PAINT message.\r
3928 |\r
3929 \*---------------------------------------------------------------------------*/\r
3930 VOID\r
3931 PaintProc(HWND hwnd)\r
3932 {\r
3933   HDC         hdc;\r
3934   PAINTSTRUCT ps;\r
3935   HFONT       oldFont;\r
3936 \r
3937   if((hdc = BeginPaint(hwnd, &ps))) {\r
3938     if (IsIconic(hwnd)) {\r
3939       DrawIcon(hdc, 2, 2, iconCurrent);\r
3940     } else {\r
3941       if (!appData.monoMode) {\r
3942         SelectPalette(hdc, hPal, FALSE);\r
3943         RealizePalette(hdc);\r
3944       }\r
3945       HDCDrawPosition(hdc, 1, NULL);\r
3946       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3947         flipView = !flipView; partnerUp = !partnerUp;\r
3948         HDCDrawPosition(hdc, 1, NULL);\r
3949         flipView = !flipView; partnerUp = !partnerUp;\r
3950       }\r
3951       oldFont =\r
3952         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3953       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3954                  ETO_CLIPPED|ETO_OPAQUE,\r
3955                  &messageRect, messageText, strlen(messageText), NULL);\r
3956       SelectObject(hdc, oldFont);\r
3957       DisplayBothClocks();\r
3958     }\r
3959     EndPaint(hwnd,&ps);\r
3960   }\r
3961 \r
3962   return;\r
3963 }\r
3964 \r
3965 \r
3966 /*\r
3967  * If the user selects on a border boundary, return -1; if off the board,\r
3968  *   return -2.  Otherwise map the event coordinate to the square.\r
3969  * The offset boardRect.left or boardRect.top must already have been\r
3970  *   subtracted from x.\r
3971  */\r
3972 int EventToSquare(x, limit)\r
3973      int x, limit;\r
3974 {\r
3975   if (x <= 0)\r
3976     return -2;\r
3977   if (x < lineGap)\r
3978     return -1;\r
3979   x -= lineGap;\r
3980   if ((x % (squareSize + lineGap)) >= squareSize)\r
3981     return -1;\r
3982   x /= (squareSize + lineGap);\r
3983     if (x >= limit)\r
3984     return -2;\r
3985   return x;\r
3986 }\r
3987 \r
3988 typedef struct {\r
3989   char piece;\r
3990   int command;\r
3991   char* name;\r
3992 } DropEnable;\r
3993 \r
3994 DropEnable dropEnables[] = {\r
3995   { 'P', DP_Pawn, N_("Pawn") },\r
3996   { 'N', DP_Knight, N_("Knight") },\r
3997   { 'B', DP_Bishop, N_("Bishop") },\r
3998   { 'R', DP_Rook, N_("Rook") },\r
3999   { 'Q', DP_Queen, N_("Queen") },\r
4000 };\r
4001 \r
4002 VOID\r
4003 SetupDropMenu(HMENU hmenu)\r
4004 {\r
4005   int i, count, enable;\r
4006   char *p;\r
4007   extern char white_holding[], black_holding[];\r
4008   char item[MSG_SIZ];\r
4009 \r
4010   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4011     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4012                dropEnables[i].piece);\r
4013     count = 0;\r
4014     while (p && *p++ == dropEnables[i].piece) count++;\r
4015       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4016     enable = count > 0 || !appData.testLegality\r
4017       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4018                       && !appData.icsActive);\r
4019     ModifyMenu(hmenu, dropEnables[i].command,\r
4020                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4021                dropEnables[i].command, item);\r
4022   }\r
4023 }\r
4024 \r
4025 void DragPieceBegin(int x, int y)\r
4026 {\r
4027       dragInfo.lastpos.x = boardRect.left + x;\r
4028       dragInfo.lastpos.y = boardRect.top + y;\r
4029       dragInfo.from.x = fromX;\r
4030       dragInfo.from.y = fromY;\r
4031       dragInfo.start = dragInfo.from;\r
4032       SetCapture(hwndMain);\r
4033 }\r
4034 \r
4035 void DragPieceEnd(int x, int y)\r
4036 {\r
4037     ReleaseCapture();\r
4038     dragInfo.start.x = dragInfo.start.y = -1;\r
4039     dragInfo.from = dragInfo.start;\r
4040     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4041 }\r
4042 \r
4043 /* Event handler for mouse messages */\r
4044 VOID\r
4045 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4046 {\r
4047   int x, y, menuNr;\r
4048   POINT pt;\r
4049   static int recursive = 0;\r
4050   HMENU hmenu;\r
4051   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4052 \r
4053   if (recursive) {\r
4054     if (message == WM_MBUTTONUP) {\r
4055       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4056          to the middle button: we simulate pressing the left button too!\r
4057          */\r
4058       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4059       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4060     }\r
4061     return;\r
4062   }\r
4063   recursive++;\r
4064   \r
4065   pt.x = LOWORD(lParam);\r
4066   pt.y = HIWORD(lParam);\r
4067   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4068   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4069   if (!flipView && y >= 0) {\r
4070     y = BOARD_HEIGHT - 1 - y;\r
4071   }\r
4072   if (flipView && x >= 0) {\r
4073     x = BOARD_WIDTH - 1 - x;\r
4074   }\r
4075 \r
4076   switch (message) {\r
4077   case WM_LBUTTONDOWN:\r
4078       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4079         if (gameMode == EditPosition) {\r
4080           SetWhiteToPlayEvent();\r
4081         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4082           AdjustClock(flipClock, -1);\r
4083         } else if (gameMode == IcsPlayingBlack ||\r
4084                    gameMode == MachinePlaysWhite) {\r
4085           CallFlagEvent();\r
4086         }\r
4087       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4088         if (gameMode == EditPosition) {\r
4089           SetBlackToPlayEvent();\r
4090         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4091           AdjustClock(!flipClock, -1);\r
4092         } else if (gameMode == IcsPlayingWhite ||\r
4093                    gameMode == MachinePlaysBlack) {\r
4094           CallFlagEvent();\r
4095         }\r
4096       }\r
4097       dragInfo.start.x = dragInfo.start.y = -1;\r
4098       dragInfo.from = dragInfo.start;\r
4099     if(fromX == -1 && frozen) { // not sure where this is for\r
4100                 fromX = fromY = -1; \r
4101       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4102       break;\r
4103     }\r
4104       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4105       DrawPosition(TRUE, NULL);\r
4106     break;\r
4107 \r
4108   case WM_LBUTTONUP:\r
4109       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4110       DrawPosition(TRUE, NULL);\r
4111     break;\r
4112 \r
4113   case WM_MOUSEMOVE:\r
4114     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4115     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4116     if ((appData.animateDragging || appData.highlightDragging)\r
4117         && (wParam & MK_LBUTTON)\r
4118         && dragInfo.from.x >= 0) \r
4119     {\r
4120       BOOL full_repaint = FALSE;\r
4121 \r
4122       if (appData.animateDragging) {\r
4123         dragInfo.pos = pt;\r
4124       }\r
4125       if (appData.highlightDragging) {\r
4126         SetHighlights(fromX, fromY, x, y);\r
4127         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4128             full_repaint = TRUE;\r
4129         }\r
4130       }\r
4131       \r
4132       DrawPosition( full_repaint, NULL);\r
4133       \r
4134       dragInfo.lastpos = dragInfo.pos;\r
4135     }\r
4136     break;\r
4137 \r
4138   case WM_MOUSEWHEEL: // [DM]\r
4139     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4140        /* Mouse Wheel is being rolled forward\r
4141         * Play moves forward\r
4142         */\r
4143        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4144                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4145        /* Mouse Wheel is being rolled backward\r
4146         * Play moves backward\r
4147         */\r
4148        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4149                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4150     }\r
4151     break;\r
4152 \r
4153   case WM_MBUTTONUP:\r
4154   case WM_RBUTTONUP:\r
4155     ReleaseCapture();\r
4156     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4157     break;\r
4158  \r
4159   case WM_MBUTTONDOWN:\r
4160   case WM_RBUTTONDOWN:\r
4161     ErrorPopDown();\r
4162     ReleaseCapture();\r
4163     fromX = fromY = -1;\r
4164     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4165     dragInfo.start.x = dragInfo.start.y = -1;\r
4166     dragInfo.from = dragInfo.start;\r
4167     dragInfo.lastpos = dragInfo.pos;\r
4168     if (appData.highlightDragging) {\r
4169       ClearHighlights();\r
4170     }\r
4171     if(y == -2) {\r
4172       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4173       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4174           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4175       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4176           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4177       }\r
4178       break;\r
4179     }\r
4180     DrawPosition(TRUE, NULL);\r
4181 \r
4182     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4183     switch (menuNr) {\r
4184     case 0:\r
4185       if (message == WM_MBUTTONDOWN) {\r
4186         buttonCount = 3;  /* even if system didn't think so */\r
4187         if (wParam & MK_SHIFT) \r
4188           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4189         else\r
4190           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4191       } else { /* message == WM_RBUTTONDOWN */\r
4192         /* Just have one menu, on the right button.  Windows users don't\r
4193            think to try the middle one, and sometimes other software steals\r
4194            it, or it doesn't really exist. */\r
4195         if(gameInfo.variant != VariantShogi)\r
4196             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4197         else\r
4198             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4199       }\r
4200       break;\r
4201     case 2:\r
4202       SetCapture(hwndMain);
4203       break;\r
4204     case 1:\r
4205       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4206       SetupDropMenu(hmenu);\r
4207       MenuPopup(hwnd, pt, hmenu, -1);\r
4208     default:\r
4209       break;\r
4210     }\r
4211     break;\r
4212   }\r
4213 \r
4214   recursive--;\r
4215 }\r
4216 \r
4217 /* Preprocess messages for buttons in main window */\r
4218 LRESULT CALLBACK\r
4219 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4220 {\r
4221   int id = GetWindowLong(hwnd, GWL_ID);\r
4222   int i, dir;\r
4223 \r
4224   for (i=0; i<N_BUTTONS; i++) {\r
4225     if (buttonDesc[i].id == id) break;\r
4226   }\r
4227   if (i == N_BUTTONS) return 0;\r
4228   switch (message) {\r
4229   case WM_KEYDOWN:\r
4230     switch (wParam) {\r
4231     case VK_LEFT:\r
4232     case VK_RIGHT:\r
4233       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4234       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4235       return TRUE;\r
4236     }\r
4237     break;\r
4238   case WM_CHAR:\r
4239     switch (wParam) {\r
4240     case '\r':\r
4241       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4242       return TRUE;\r
4243     default:\r
4244       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4245         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4246         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4247         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4248         SetFocus(h);\r
4249         SendMessage(h, WM_CHAR, wParam, lParam);\r
4250         return TRUE;\r
4251       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4252         PopUpMoveDialog((char)wParam);\r
4253       }\r
4254       break;\r
4255     }\r
4256     break;\r
4257   }\r
4258   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4259 }\r
4260 \r
4261 /* Process messages for Promotion dialog box */\r
4262 LRESULT CALLBACK\r
4263 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4264 {\r
4265   char promoChar;\r
4266 \r
4267   switch (message) {\r
4268   case WM_INITDIALOG: /* message: initialize dialog box */\r
4269     /* Center the dialog over the application window */\r
4270     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4271     Translate(hDlg, DLG_PromotionKing);\r
4272     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4273       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4274        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4275                SW_SHOW : SW_HIDE);\r
4276     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4277     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4278        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4279          PieceToChar(WhiteAngel) != '~') ||\r
4280         (PieceToChar(BlackAngel) >= 'A' &&\r
4281          PieceToChar(BlackAngel) != '~')   ) ?\r
4282                SW_SHOW : SW_HIDE);\r
4283     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4284        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4285          PieceToChar(WhiteMarshall) != '~') ||\r
4286         (PieceToChar(BlackMarshall) >= 'A' &&\r
4287          PieceToChar(BlackMarshall) != '~')   ) ?\r
4288                SW_SHOW : SW_HIDE);\r
4289     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4290     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4291        gameInfo.variant != VariantShogi ?\r
4292                SW_SHOW : SW_HIDE);\r
4293     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4294        gameInfo.variant != VariantShogi ?\r
4295                SW_SHOW : SW_HIDE);\r
4296     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4297        gameInfo.variant == VariantShogi ?\r
4298                SW_SHOW : SW_HIDE);\r
4299     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4300        gameInfo.variant == VariantShogi ?\r
4301                SW_SHOW : SW_HIDE);\r
4302     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4303        gameInfo.variant == VariantSuper ?\r
4304                SW_SHOW : SW_HIDE);\r
4305     return TRUE;\r
4306 \r
4307   case WM_COMMAND: /* message: received a command */\r
4308     switch (LOWORD(wParam)) {\r
4309     case IDCANCEL:\r
4310       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4311       ClearHighlights();\r
4312       DrawPosition(FALSE, NULL);\r
4313       return TRUE;\r
4314     case PB_King:\r
4315       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4316       break;\r
4317     case PB_Queen:\r
4318       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4319       break;\r
4320     case PB_Rook:\r
4321       promoChar = PieceToChar(BlackRook);\r
4322       break;\r
4323     case PB_Bishop:\r
4324       promoChar = PieceToChar(BlackBishop);\r
4325       break;\r
4326     case PB_Chancellor:\r
4327       promoChar = PieceToChar(BlackMarshall);\r
4328       break;\r
4329     case PB_Archbishop:\r
4330       promoChar = PieceToChar(BlackAngel);\r
4331       break;\r
4332     case PB_Knight:\r
4333       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4334       break;\r
4335     default:\r
4336       return FALSE;\r
4337     }\r
4338     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4339     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4340        only show the popup when we are already sure the move is valid or\r
4341        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4342        will figure out it is a promotion from the promoChar. */\r
4343     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4344     fromX = fromY = -1;\r
4345     if (!appData.highlightLastMove) {\r
4346       ClearHighlights();\r
4347       DrawPosition(FALSE, NULL);\r
4348     }\r
4349     return TRUE;\r
4350   }\r
4351   return FALSE;\r
4352 }\r
4353 \r
4354 /* Pop up promotion dialog */\r
4355 VOID\r
4356 PromotionPopup(HWND hwnd)\r
4357 {\r
4358   FARPROC lpProc;\r
4359 \r
4360   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4361   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4362     hwnd, (DLGPROC)lpProc);\r
4363   FreeProcInstance(lpProc);\r
4364 }\r
4365 \r
4366 void\r
4367 PromotionPopUp()\r
4368 {\r
4369   DrawPosition(TRUE, NULL);\r
4370   PromotionPopup(hwndMain);\r
4371 }\r
4372 \r
4373 /* Toggle ShowThinking */\r
4374 VOID\r
4375 ToggleShowThinking()\r
4376 {\r
4377   appData.showThinking = !appData.showThinking;\r
4378   ShowThinkingEvent();\r
4379 }\r
4380 \r
4381 VOID\r
4382 LoadGameDialog(HWND hwnd, char* title)\r
4383 {\r
4384   UINT number = 0;\r
4385   FILE *f;\r
4386   char fileTitle[MSG_SIZ];\r
4387   f = OpenFileDialog(hwnd, "rb", "",\r
4388                      appData.oldSaveStyle ? "gam" : "pgn",\r
4389                      GAME_FILT,\r
4390                      title, &number, fileTitle, NULL);\r
4391   if (f != NULL) {\r
4392     cmailMsgLoaded = FALSE;\r
4393     if (number == 0) {\r
4394       int error = GameListBuild(f);\r
4395       if (error) {\r
4396         DisplayError(_("Cannot build game list"), error);\r
4397       } else if (!ListEmpty(&gameList) &&\r
4398                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4399         GameListPopUp(f, fileTitle);\r
4400         return;\r
4401       }\r
4402       GameListDestroy();\r
4403       number = 1;\r
4404     }\r
4405     LoadGame(f, number, fileTitle, FALSE);\r
4406   }\r
4407 }\r
4408 \r
4409 int get_term_width()\r
4410 {\r
4411     HDC hdc;\r
4412     TEXTMETRIC tm;\r
4413     RECT rc;\r
4414     HFONT hfont, hold_font;\r
4415     LOGFONT lf;\r
4416     HWND hText;\r
4417 \r
4418     if (hwndConsole)\r
4419         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4420     else\r
4421         return 79;\r
4422 \r
4423     // get the text metrics\r
4424     hdc = GetDC(hText);\r
4425     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4426     if (consoleCF.dwEffects & CFE_BOLD)\r
4427         lf.lfWeight = FW_BOLD;\r
4428     if (consoleCF.dwEffects & CFE_ITALIC)\r
4429         lf.lfItalic = TRUE;\r
4430     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4431         lf.lfStrikeOut = TRUE;\r
4432     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4433         lf.lfUnderline = TRUE;\r
4434     hfont = CreateFontIndirect(&lf);\r
4435     hold_font = SelectObject(hdc, hfont);\r
4436     GetTextMetrics(hdc, &tm);\r
4437     SelectObject(hdc, hold_font);\r
4438     DeleteObject(hfont);\r
4439     ReleaseDC(hText, hdc);\r
4440 \r
4441     // get the rectangle\r
4442     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4443 \r
4444     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4445 }\r
4446 \r
4447 void UpdateICSWidth(HWND hText)\r
4448 {\r
4449     LONG old_width, new_width;\r
4450 \r
4451     new_width = get_term_width(hText, FALSE);\r
4452     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4453     if (new_width != old_width)\r
4454     {\r
4455         ics_update_width(new_width);\r
4456         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4457     }\r
4458 }\r
4459 \r
4460 VOID\r
4461 ChangedConsoleFont()\r
4462 {\r
4463   CHARFORMAT cfmt;\r
4464   CHARRANGE tmpsel, sel;\r
4465   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4466   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4467   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4468   PARAFORMAT paraf;\r
4469 \r
4470   cfmt.cbSize = sizeof(CHARFORMAT);\r
4471   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4472     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4473                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4474   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4475    * size.  This was undocumented in the version of MSVC++ that I had\r
4476    * when I wrote the code, but is apparently documented now.\r
4477    */\r
4478   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4479   cfmt.bCharSet = f->lf.lfCharSet;\r
4480   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4481   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4482   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4483   /* Why are the following seemingly needed too? */\r
4484   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4485   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4486   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4487   tmpsel.cpMin = 0;\r
4488   tmpsel.cpMax = -1; /*999999?*/\r
4489   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4490   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4491   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4492    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4493    */\r
4494   paraf.cbSize = sizeof(paraf);\r
4495   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4496   paraf.dxStartIndent = 0;\r
4497   paraf.dxOffset = WRAP_INDENT;\r
4498   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4499   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4500   UpdateICSWidth(hText);\r
4501 }\r
4502 \r
4503 /*---------------------------------------------------------------------------*\\r
4504  *\r
4505  * Window Proc for main window\r
4506  *\r
4507 \*---------------------------------------------------------------------------*/\r
4508 \r
4509 /* Process messages for main window, etc. */\r
4510 LRESULT CALLBACK\r
4511 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4512 {\r
4513   FARPROC lpProc;\r
4514   int wmId, wmEvent;\r
4515   char *defName;\r
4516   FILE *f;\r
4517   UINT number;\r
4518   char fileTitle[MSG_SIZ];\r
4519   char buf[MSG_SIZ];\r
4520   static SnapData sd;\r
4521 \r
4522   switch (message) {\r
4523 \r
4524   case WM_PAINT: /* message: repaint portion of window */\r
4525     PaintProc(hwnd);\r
4526     break;\r
4527 \r
4528   case WM_ERASEBKGND:\r
4529     if (IsIconic(hwnd)) {\r
4530       /* Cheat; change the message */\r
4531       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4532     } else {\r
4533       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4534     }\r
4535     break;\r
4536 \r
4537   case WM_LBUTTONDOWN:\r
4538   case WM_MBUTTONDOWN:\r
4539   case WM_RBUTTONDOWN:\r
4540   case WM_LBUTTONUP:\r
4541   case WM_MBUTTONUP:\r
4542   case WM_RBUTTONUP:\r
4543   case WM_MOUSEMOVE:\r
4544   case WM_MOUSEWHEEL:\r
4545     MouseEvent(hwnd, message, wParam, lParam);\r
4546     break;\r
4547 \r
4548   JAWS_KB_NAVIGATION\r
4549 \r
4550   case WM_CHAR:\r
4551     \r
4552     JAWS_ALT_INTERCEPT\r
4553 \r
4554     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4555         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4556         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4557         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4558         SetFocus(h);\r
4559         SendMessage(h, message, wParam, lParam);\r
4560     } else if(lParam != KF_REPEAT) {\r
4561         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4562                 PopUpMoveDialog((char)wParam);\r
4563         } else if((char)wParam == 003) CopyGameToClipboard();\r
4564          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4565     }\r
4566 \r
4567     break;\r
4568 \r
4569   case WM_PALETTECHANGED:\r
4570     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4571       int nnew;\r
4572       HDC hdc = GetDC(hwndMain);\r
4573       SelectPalette(hdc, hPal, TRUE);\r
4574       nnew = RealizePalette(hdc);\r
4575       if (nnew > 0) {\r
4576         paletteChanged = TRUE;\r
4577         InvalidateRect(hwnd, &boardRect, FALSE);\r
4578       }\r
4579       ReleaseDC(hwnd, hdc);\r
4580     }\r
4581     break;\r
4582 \r
4583   case WM_QUERYNEWPALETTE:\r
4584     if (!appData.monoMode /*&& paletteChanged*/) {\r
4585       int nnew;\r
4586       HDC hdc = GetDC(hwndMain);\r
4587       paletteChanged = FALSE;\r
4588       SelectPalette(hdc, hPal, FALSE);\r
4589       nnew = RealizePalette(hdc);\r
4590       if (nnew > 0) {\r
4591         InvalidateRect(hwnd, &boardRect, FALSE);\r
4592       }\r
4593       ReleaseDC(hwnd, hdc);\r
4594       return TRUE;\r
4595     }\r
4596     return FALSE;\r
4597 \r
4598   case WM_COMMAND: /* message: command from application menu */\r
4599     wmId    = LOWORD(wParam);\r
4600     wmEvent = HIWORD(wParam);\r
4601 \r
4602     switch (wmId) {\r
4603     case IDM_NewGame:\r
4604       ResetGameEvent();\r
4605       SAY("new game enter a move to play against the computer with white");\r
4606       break;\r
4607 \r
4608     case IDM_NewGameFRC:\r
4609       if( NewGameFRC() == 0 ) {\r
4610         ResetGameEvent();\r
4611       }\r
4612       break;\r
4613 \r
4614     case IDM_NewVariant:\r
4615       NewVariantPopup(hwnd);\r
4616       break;\r
4617 \r
4618     case IDM_LoadGame:\r
4619       LoadGameDialog(hwnd, _("Load Game from File"));\r
4620       break;\r
4621 \r
4622     case IDM_LoadNextGame:\r
4623       ReloadGame(1);\r
4624       break;\r
4625 \r
4626     case IDM_LoadPrevGame:\r
4627       ReloadGame(-1);\r
4628       break;\r
4629 \r
4630     case IDM_ReloadGame:\r
4631       ReloadGame(0);\r
4632       break;\r
4633 \r
4634     case IDM_LoadPosition:\r
4635       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4636         Reset(FALSE, TRUE);\r
4637       }\r
4638       number = 1;\r
4639       f = OpenFileDialog(hwnd, "rb", "",\r
4640                          appData.oldSaveStyle ? "pos" : "fen",\r
4641                          POSITION_FILT,\r
4642                          _("Load Position from File"), &number, fileTitle, NULL);\r
4643       if (f != NULL) {\r
4644         LoadPosition(f, number, fileTitle);\r
4645       }\r
4646       break;\r
4647 \r
4648     case IDM_LoadNextPosition:\r
4649       ReloadPosition(1);\r
4650       break;\r
4651 \r
4652     case IDM_LoadPrevPosition:\r
4653       ReloadPosition(-1);\r
4654       break;\r
4655 \r
4656     case IDM_ReloadPosition:\r
4657       ReloadPosition(0);\r
4658       break;\r
4659 \r
4660     case IDM_SaveGame:\r
4661       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4662       f = OpenFileDialog(hwnd, "a", defName,\r
4663                          appData.oldSaveStyle ? "gam" : "pgn",\r
4664                          GAME_FILT,\r
4665                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4666       if (f != NULL) {\r
4667         SaveGame(f, 0, "");\r
4668       }\r
4669       break;\r
4670 \r
4671     case IDM_SavePosition:\r
4672       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4673       f = OpenFileDialog(hwnd, "a", defName,\r
4674                          appData.oldSaveStyle ? "pos" : "fen",\r
4675                          POSITION_FILT,\r
4676                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4677       if (f != NULL) {\r
4678         SavePosition(f, 0, "");\r
4679       }\r
4680       break;\r
4681 \r
4682     case IDM_SaveDiagram:\r
4683       defName = "diagram";\r
4684       f = OpenFileDialog(hwnd, "wb", defName,\r
4685                          "bmp",\r
4686                          DIAGRAM_FILT,\r
4687                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4688       if (f != NULL) {\r
4689         SaveDiagram(f);\r
4690       }\r
4691       break;\r
4692 \r
4693     case IDM_CopyGame:\r
4694       CopyGameToClipboard();\r
4695       break;\r
4696 \r
4697     case IDM_PasteGame:\r
4698       PasteGameFromClipboard();\r
4699       break;\r
4700 \r
4701     case IDM_CopyGameListToClipboard:\r
4702       CopyGameListToClipboard();\r
4703       break;\r
4704 \r
4705     /* [AS] Autodetect FEN or PGN data */\r
4706     case IDM_PasteAny:\r
4707       PasteGameOrFENFromClipboard();\r
4708       break;\r
4709 \r
4710     /* [AS] Move history */\r
4711     case IDM_ShowMoveHistory:\r
4712         if( MoveHistoryIsUp() ) {\r
4713             MoveHistoryPopDown();\r
4714         }\r
4715         else {\r
4716             MoveHistoryPopUp();\r
4717         }\r
4718         break;\r
4719 \r
4720     /* [AS] Eval graph */\r
4721     case IDM_ShowEvalGraph:\r
4722         if( EvalGraphIsUp() ) {\r
4723             EvalGraphPopDown();\r
4724         }\r
4725         else {\r
4726             EvalGraphPopUp();\r
4727             SetFocus(hwndMain);\r
4728         }\r
4729         break;\r
4730 \r
4731     /* [AS] Engine output */\r
4732     case IDM_ShowEngineOutput:\r
4733         if( EngineOutputIsUp() ) {\r
4734             EngineOutputPopDown();\r
4735         }\r
4736         else {\r
4737             EngineOutputPopUp();\r
4738         }\r
4739         break;\r
4740 \r
4741     /* [AS] User adjudication */\r
4742     case IDM_UserAdjudication_White:\r
4743         UserAdjudicationEvent( +1 );\r
4744         break;\r
4745 \r
4746     case IDM_UserAdjudication_Black:\r
4747         UserAdjudicationEvent( -1 );\r
4748         break;\r
4749 \r
4750     case IDM_UserAdjudication_Draw:\r
4751         UserAdjudicationEvent( 0 );\r
4752         break;\r
4753 \r
4754     /* [AS] Game list options dialog */\r
4755     case IDM_GameListOptions:\r
4756       GameListOptions();\r
4757       break;\r
4758 \r
4759     case IDM_NewChat:\r
4760       ChatPopUp(NULL);\r
4761       break;\r
4762 \r
4763     case IDM_CopyPosition:\r
4764       CopyFENToClipboard();\r
4765       break;\r
4766 \r
4767     case IDM_PastePosition:\r
4768       PasteFENFromClipboard();\r
4769       break;\r
4770 \r
4771     case IDM_MailMove:\r
4772       MailMoveEvent();\r
4773       break;\r
4774 \r
4775     case IDM_ReloadCMailMsg:\r
4776       Reset(TRUE, TRUE);\r
4777       ReloadCmailMsgEvent(FALSE);\r
4778       break;\r
4779 \r
4780     case IDM_Minimize:\r
4781       ShowWindow(hwnd, SW_MINIMIZE);\r
4782       break;\r
4783 \r
4784     case IDM_Exit:\r
4785       ExitEvent(0);\r
4786       break;\r
4787 \r
4788     case IDM_MachineWhite:\r
4789       MachineWhiteEvent();\r
4790       /*\r
4791        * refresh the tags dialog only if it's visible\r
4792        */\r
4793       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4794           char *tags;\r
4795           tags = PGNTags(&gameInfo);\r
4796           TagsPopUp(tags, CmailMsg());\r
4797           free(tags);\r
4798       }\r
4799       SAY("computer starts playing white");\r
4800       break;\r
4801 \r
4802     case IDM_MachineBlack:\r
4803       MachineBlackEvent();\r
4804       /*\r
4805        * refresh the tags dialog only if it's visible\r
4806        */\r
4807       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4808           char *tags;\r
4809           tags = PGNTags(&gameInfo);\r
4810           TagsPopUp(tags, CmailMsg());\r
4811           free(tags);\r
4812       }\r
4813       SAY("computer starts playing black");\r
4814       break;\r
4815 \r
4816     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4817       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4818       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4819       appData.matchGames = appData.defaultMatchGames;\r
4820       matchGame = 1;\r
4821 \r
4822     case IDM_TwoMachines:\r
4823       TwoMachinesEvent();\r
4824       /*\r
4825        * refresh the tags dialog only if it's visible\r
4826        */\r
4827       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4828           char *tags;\r
4829           tags = PGNTags(&gameInfo);\r
4830           TagsPopUp(tags, CmailMsg());\r
4831           free(tags);\r
4832       }\r
4833       SAY("computer starts playing both sides");\r
4834       break;\r
4835 \r
4836     case IDM_AnalysisMode:\r
4837       if (!first.analysisSupport) {\r
4838         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4839         DisplayError(buf, 0);\r
4840       } else {\r
4841         SAY("analyzing current position");\r
4842         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4843         if (appData.icsActive) {\r
4844                if (gameMode != IcsObserving) {\r
4845                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4846                        DisplayError(buf, 0);\r
4847                        /* secure check */\r
4848                        if (appData.icsEngineAnalyze) {\r
4849                                if (appData.debugMode) \r
4850                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4851                                ExitAnalyzeMode();\r
4852                                ModeHighlight();\r
4853                                break;\r
4854                        }\r
4855                        break;\r
4856                } else {\r
4857                        /* if enable, user want disable icsEngineAnalyze */\r
4858                        if (appData.icsEngineAnalyze) {\r
4859                                ExitAnalyzeMode();\r
4860                                ModeHighlight();\r
4861                                break;\r
4862                        }\r
4863                        appData.icsEngineAnalyze = TRUE;\r
4864                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4865                }\r
4866         } \r
4867         if (!appData.showThinking) ToggleShowThinking();\r
4868         AnalyzeModeEvent();\r
4869       }\r
4870       break;\r
4871 \r
4872     case IDM_AnalyzeFile:\r
4873       if (!first.analysisSupport) {\r
4874         char buf[MSG_SIZ];\r
4875           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4876         DisplayError(buf, 0);\r
4877       } else {\r
4878         if (!appData.showThinking) ToggleShowThinking();\r
4879         AnalyzeFileEvent();\r
4880         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4881         AnalysisPeriodicEvent(1);\r
4882       }\r
4883       break;\r
4884 \r
4885     case IDM_IcsClient:\r
4886       IcsClientEvent();\r
4887       break;\r
4888 \r
4889     case IDM_EditGame:\r
4890       EditGameEvent();\r
4891       SAY("edit game");\r
4892       break;\r
4893 \r
4894     case IDM_EditPosition:\r
4895       EditPositionEvent();\r
4896       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4897       break;\r
4898 \r
4899     case IDM_Training:\r
4900       TrainingEvent();\r
4901       break;\r
4902 \r
4903     case IDM_ShowGameList:\r
4904       ShowGameListProc();\r
4905       break;\r
4906 \r
4907     case IDM_EditTags:\r
4908       EditTagsProc();\r
4909       break;\r
4910 \r
4911     case IDM_EditComment:\r
4912       if (commentUp && editComment) {\r
4913         CommentPopDown();\r
4914       } else {\r
4915         EditCommentEvent();\r
4916       }\r
4917       break;\r
4918 \r
4919     case IDM_Pause:\r
4920       PauseEvent();\r
4921       break;\r
4922 \r
4923     case IDM_Accept:\r
4924       AcceptEvent();\r
4925       break;\r
4926 \r
4927     case IDM_Decline:\r
4928       DeclineEvent();\r
4929       break;\r
4930 \r
4931     case IDM_Rematch:\r
4932       RematchEvent();\r
4933       break;\r
4934 \r
4935     case IDM_CallFlag:\r
4936       CallFlagEvent();\r
4937       break;\r
4938 \r
4939     case IDM_Draw:\r
4940       DrawEvent();\r
4941       break;\r
4942 \r
4943     case IDM_Adjourn:\r
4944       AdjournEvent();\r
4945       break;\r
4946 \r
4947     case IDM_Abort:\r
4948       AbortEvent();\r
4949       break;\r
4950 \r
4951     case IDM_Resign:\r
4952       ResignEvent();\r
4953       break;\r
4954 \r
4955     case IDM_StopObserving:\r
4956       StopObservingEvent();\r
4957       break;\r
4958 \r
4959     case IDM_StopExamining:\r
4960       StopExaminingEvent();\r
4961       break;\r
4962 \r
4963     case IDM_Upload:\r
4964       UploadGameEvent();\r
4965       break;\r
4966 \r
4967     case IDM_TypeInMove:\r
4968       PopUpMoveDialog('\000');\r
4969       break;\r
4970 \r
4971     case IDM_TypeInName:\r
4972       PopUpNameDialog('\000');\r
4973       break;\r
4974 \r
4975     case IDM_Backward:\r
4976       BackwardEvent();\r
4977       SetFocus(hwndMain);\r
4978       break;\r
4979 \r
4980     JAWS_MENU_ITEMS\r
4981 \r
4982     case IDM_Forward:\r
4983       ForwardEvent();\r
4984       SetFocus(hwndMain);\r
4985       break;\r
4986 \r
4987     case IDM_ToStart:\r
4988       ToStartEvent();\r
4989       SetFocus(hwndMain);\r
4990       break;\r
4991 \r
4992     case IDM_ToEnd:\r
4993       ToEndEvent();\r
4994       SetFocus(hwndMain);\r
4995       break;\r
4996 \r
4997     case IDM_Revert:\r
4998       RevertEvent(FALSE);\r
4999       break;\r
5000 \r
5001     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5002       RevertEvent(TRUE);\r
5003       break;\r
5004 \r
5005     case IDM_TruncateGame:\r
5006       TruncateGameEvent();\r
5007       break;\r
5008 \r
5009     case IDM_MoveNow:\r
5010       MoveNowEvent();\r
5011       break;\r
5012 \r
5013     case IDM_RetractMove:\r
5014       RetractMoveEvent();\r
5015       break;\r
5016 \r
5017     case IDM_FlipView:\r
5018       flipView = !flipView;\r
5019       DrawPosition(FALSE, NULL);\r
5020       break;\r
5021 \r
5022     case IDM_FlipClock:\r
5023       flipClock = !flipClock;\r
5024       DisplayBothClocks();\r
5025       DrawPosition(FALSE, NULL);\r
5026       break;\r
5027 \r
5028     case IDM_MuteSounds:\r
5029       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5030       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5031                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5032       break;\r
5033 \r
5034     case IDM_GeneralOptions:\r
5035       GeneralOptionsPopup(hwnd);\r
5036       DrawPosition(TRUE, NULL);\r
5037       break;\r
5038 \r
5039     case IDM_BoardOptions:\r
5040       BoardOptionsPopup(hwnd);\r
5041       break;\r
5042 \r
5043     case IDM_EnginePlayOptions:\r
5044       EnginePlayOptionsPopup(hwnd);\r
5045       break;\r
5046 \r
5047     case IDM_Engine1Options:\r
5048       EngineOptionsPopup(hwnd, &first);\r
5049       break;\r
5050 \r
5051     case IDM_Engine2Options:\r
5052       EngineOptionsPopup(hwnd, &second);\r
5053       break;\r
5054 \r
5055     case IDM_OptionsUCI:\r
5056       UciOptionsPopup(hwnd);\r
5057       break;\r
5058 \r
5059     case IDM_IcsOptions:\r
5060       IcsOptionsPopup(hwnd);\r
5061       break;\r
5062 \r
5063     case IDM_Fonts:\r
5064       FontsOptionsPopup(hwnd);\r
5065       break;\r
5066 \r
5067     case IDM_Sounds:\r
5068       SoundOptionsPopup(hwnd);\r
5069       break;\r
5070 \r
5071     case IDM_CommPort:\r
5072       CommPortOptionsPopup(hwnd);\r
5073       break;\r
5074 \r
5075     case IDM_LoadOptions:\r
5076       LoadOptionsPopup(hwnd);\r
5077       break;\r
5078 \r
5079     case IDM_SaveOptions:\r
5080       SaveOptionsPopup(hwnd);\r
5081       break;\r
5082 \r
5083     case IDM_TimeControl:\r
5084       TimeControlOptionsPopup(hwnd);\r
5085       break;\r
5086 \r
5087     case IDM_SaveSettings:\r
5088       SaveSettings(settingsFileName);\r
5089       break;\r
5090 \r
5091     case IDM_SaveSettingsOnExit:\r
5092       saveSettingsOnExit = !saveSettingsOnExit;\r
5093       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5094                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5095                                          MF_CHECKED : MF_UNCHECKED));\r
5096       break;\r
5097 \r
5098     case IDM_Hint:\r
5099       HintEvent();\r
5100       break;\r
5101 \r
5102     case IDM_Book:\r
5103       BookEvent();\r
5104       break;\r
5105 \r
5106     case IDM_AboutGame:\r
5107       AboutGameEvent();\r
5108       break;\r
5109 \r
5110     case IDM_Debug:\r
5111       appData.debugMode = !appData.debugMode;\r
5112       if (appData.debugMode) {\r
5113         char dir[MSG_SIZ];\r
5114         GetCurrentDirectory(MSG_SIZ, dir);\r
5115         SetCurrentDirectory(installDir);\r
5116         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5117         SetCurrentDirectory(dir);\r
5118         setbuf(debugFP, NULL);\r
5119       } else {\r
5120         fclose(debugFP);\r
5121         debugFP = NULL;\r
5122       }\r
5123       break;\r
5124 \r
5125     case IDM_HELPCONTENTS:\r
5126       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5127           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5128           MessageBox (GetFocus(),\r
5129                     _("Unable to activate help"),\r
5130                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5131       }\r
5132       break;\r
5133 \r
5134     case IDM_HELPSEARCH:\r
5135         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5136             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5137         MessageBox (GetFocus(),\r
5138                     _("Unable to activate help"),\r
5139                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5140       }\r
5141       break;\r
5142 \r
5143     case IDM_HELPHELP:\r
5144       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5145         MessageBox (GetFocus(),\r
5146                     _("Unable to activate help"),\r
5147                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5148       }\r
5149       break;\r
5150 \r
5151     case IDM_ABOUT:\r
5152       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5153       DialogBox(hInst, \r
5154         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5155         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5156       FreeProcInstance(lpProc);\r
5157       break;\r
5158 \r
5159     case IDM_DirectCommand1:\r
5160       AskQuestionEvent(_("Direct Command"),\r
5161                        _("Send to chess program:"), "", "1");\r
5162       break;\r
5163     case IDM_DirectCommand2:\r
5164       AskQuestionEvent(_("Direct Command"),\r
5165                        _("Send to second chess program:"), "", "2");\r
5166       break;\r
5167 \r
5168     case EP_WhitePawn:\r
5169       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5170       fromX = fromY = -1;\r
5171       break;\r
5172 \r
5173     case EP_WhiteKnight:\r
5174       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5175       fromX = fromY = -1;\r
5176       break;\r
5177 \r
5178     case EP_WhiteBishop:\r
5179       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5180       fromX = fromY = -1;\r
5181       break;\r
5182 \r
5183     case EP_WhiteRook:\r
5184       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5185       fromX = fromY = -1;\r
5186       break;\r
5187 \r
5188     case EP_WhiteQueen:\r
5189       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5190       fromX = fromY = -1;\r
5191       break;\r
5192 \r
5193     case EP_WhiteFerz:\r
5194       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5195       fromX = fromY = -1;\r
5196       break;\r
5197 \r
5198     case EP_WhiteWazir:\r
5199       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5200       fromX = fromY = -1;\r
5201       break;\r
5202 \r
5203     case EP_WhiteAlfil:\r
5204       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5205       fromX = fromY = -1;\r
5206       break;\r
5207 \r
5208     case EP_WhiteCannon:\r
5209       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5210       fromX = fromY = -1;\r
5211       break;\r
5212 \r
5213     case EP_WhiteCardinal:\r
5214       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5215       fromX = fromY = -1;\r
5216       break;\r
5217 \r
5218     case EP_WhiteMarshall:\r
5219       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5220       fromX = fromY = -1;\r
5221       break;\r
5222 \r
5223     case EP_WhiteKing:\r
5224       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5225       fromX = fromY = -1;\r
5226       break;\r
5227 \r
5228     case EP_BlackPawn:\r
5229       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5230       fromX = fromY = -1;\r
5231       break;\r
5232 \r
5233     case EP_BlackKnight:\r
5234       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_BlackBishop:\r
5239       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_BlackRook:\r
5244       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_BlackQueen:\r
5249       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_BlackFerz:\r
5254       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_BlackWazir:\r
5259       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_BlackAlfil:\r
5264       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_BlackCannon:\r
5269       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_BlackCardinal:\r
5274       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_BlackMarshall:\r
5279       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_BlackKing:\r
5284       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_EmptySquare:\r
5289       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_ClearBoard:\r
5294       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_White:\r
5299       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_Black:\r
5304       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_Promote:\r
5309       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_Demote:\r
5314       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case DP_Pawn:\r
5319       DropMenuEvent(WhitePawn, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case DP_Knight:\r
5324       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case DP_Bishop:\r
5329       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case DP_Rook:\r
5334       DropMenuEvent(WhiteRook, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case DP_Queen:\r
5339       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case IDM_English:\r
5344       barbaric = 0;\r
5345       TranslateMenus(0);\r
5346       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5347       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5348       lastChecked = wmId;\r
5349       break;\r
5350 \r
5351     default:\r
5352       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5353           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5354           TranslateMenus(0);\r
5355           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5356           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5357           lastChecked = wmId;\r
5358           break;\r
5359       }\r
5360       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5361     }\r
5362     break;\r
5363 \r
5364   case WM_TIMER:\r
5365     switch (wParam) {\r
5366     case CLOCK_TIMER_ID:\r
5367       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5368       clockTimerEvent = 0;\r
5369       DecrementClocks(); /* call into back end */\r
5370       break;\r
5371     case LOAD_GAME_TIMER_ID:\r
5372       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5373       loadGameTimerEvent = 0;\r
5374       AutoPlayGameLoop(); /* call into back end */\r
5375       break;\r
5376     case ANALYSIS_TIMER_ID:\r
5377       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5378                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5379         AnalysisPeriodicEvent(0);\r
5380       } else {\r
5381         KillTimer(hwnd, analysisTimerEvent);\r
5382         analysisTimerEvent = 0;\r
5383       }\r
5384       break;\r
5385     case DELAYED_TIMER_ID:\r
5386       KillTimer(hwnd, delayedTimerEvent);\r
5387       delayedTimerEvent = 0;\r
5388       delayedTimerCallback();\r
5389       break;\r
5390     }\r
5391     break;\r
5392 \r
5393   case WM_USER_Input:\r
5394     InputEvent(hwnd, message, wParam, lParam);\r
5395     break;\r
5396 \r
5397   /* [AS] Also move "attached" child windows */\r
5398   case WM_WINDOWPOSCHANGING:\r
5399 \r
5400     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5401         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5402 \r
5403         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5404             /* Window is moving */\r
5405             RECT rcMain;\r
5406 \r
5407 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5408             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5409             rcMain.right  = wpMain.x + wpMain.width;\r
5410             rcMain.top    = wpMain.y;\r
5411             rcMain.bottom = wpMain.y + wpMain.height;\r
5412             \r
5413             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5414             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5417             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5418             wpMain.x = lpwp->x;\r
5419             wpMain.y = lpwp->y;\r
5420         }\r
5421     }\r
5422     break;\r
5423 \r
5424   /* [AS] Snapping */\r
5425   case WM_ENTERSIZEMOVE:\r
5426     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5427     if (hwnd == hwndMain) {\r
5428       doingSizing = TRUE;\r
5429       lastSizing = 0;\r
5430     }\r
5431     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5432     break;\r
5433 \r
5434   case WM_SIZING:\r
5435     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5436     if (hwnd == hwndMain) {\r
5437       lastSizing = wParam;\r
5438     }\r
5439     break;\r
5440 \r
5441   case WM_MOVING:\r
5442     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5443       return OnMoving( &sd, hwnd, wParam, lParam );\r
5444 \r
5445   case WM_EXITSIZEMOVE:\r
5446     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5447     if (hwnd == hwndMain) {\r
5448       RECT client;\r
5449       doingSizing = FALSE;\r
5450       InvalidateRect(hwnd, &boardRect, FALSE);\r
5451       GetClientRect(hwnd, &client);\r
5452       ResizeBoard(client.right, client.bottom, lastSizing);\r
5453       lastSizing = 0;\r
5454       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5455     }\r
5456     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5457     break;\r
5458 \r
5459   case WM_DESTROY: /* message: window being destroyed */\r
5460     PostQuitMessage(0);\r
5461     break;\r
5462 \r
5463   case WM_CLOSE:\r
5464     if (hwnd == hwndMain) {\r
5465       ExitEvent(0);\r
5466     }\r
5467     break;\r
5468 \r
5469   default:      /* Passes it on if unprocessed */\r
5470     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5471   }\r
5472   return 0;\r
5473 }\r
5474 \r
5475 /*---------------------------------------------------------------------------*\\r
5476  *\r
5477  * Misc utility routines\r
5478  *\r
5479 \*---------------------------------------------------------------------------*/\r
5480 \r
5481 /*\r
5482  * Decent random number generator, at least not as bad as Windows\r
5483  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5484  */\r
5485 unsigned int randstate;\r
5486 \r
5487 int\r
5488 myrandom(void)\r
5489 {\r
5490   randstate = randstate * 1664525 + 1013904223;\r
5491   return (int) randstate & 0x7fffffff;\r
5492 }\r
5493 \r
5494 void\r
5495 mysrandom(unsigned int seed)\r
5496 {\r
5497   randstate = seed;\r
5498 }\r
5499 \r
5500 \r
5501 /* \r
5502  * returns TRUE if user selects a different color, FALSE otherwise \r
5503  */\r
5504 \r
5505 BOOL\r
5506 ChangeColor(HWND hwnd, COLORREF *which)\r
5507 {\r
5508   static BOOL firstTime = TRUE;\r
5509   static DWORD customColors[16];\r
5510   CHOOSECOLOR cc;\r
5511   COLORREF newcolor;\r
5512   int i;\r
5513   ColorClass ccl;\r
5514 \r
5515   if (firstTime) {\r
5516     /* Make initial colors in use available as custom colors */\r
5517     /* Should we put the compiled-in defaults here instead? */\r
5518     i = 0;\r
5519     customColors[i++] = lightSquareColor & 0xffffff;\r
5520     customColors[i++] = darkSquareColor & 0xffffff;\r
5521     customColors[i++] = whitePieceColor & 0xffffff;\r
5522     customColors[i++] = blackPieceColor & 0xffffff;\r
5523     customColors[i++] = highlightSquareColor & 0xffffff;\r
5524     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5525 \r
5526     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5527       customColors[i++] = textAttribs[ccl].color;\r
5528     }\r
5529     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5530     firstTime = FALSE;\r
5531   }\r
5532 \r
5533   cc.lStructSize = sizeof(cc);\r
5534   cc.hwndOwner = hwnd;\r
5535   cc.hInstance = NULL;\r
5536   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5537   cc.lpCustColors = (LPDWORD) customColors;\r
5538   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5539 \r
5540   if (!ChooseColor(&cc)) return FALSE;\r
5541 \r
5542   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5543   if (newcolor == *which) return FALSE;\r
5544   *which = newcolor;\r
5545   return TRUE;\r
5546 \r
5547   /*\r
5548   InitDrawingColors();\r
5549   InvalidateRect(hwnd, &boardRect, FALSE);\r
5550   */\r
5551 }\r
5552 \r
5553 BOOLEAN\r
5554 MyLoadSound(MySound *ms)\r
5555 {\r
5556   BOOL ok = FALSE;\r
5557   struct stat st;\r
5558   FILE *f;\r
5559 \r
5560   if (ms->data) free(ms->data);\r
5561   ms->data = NULL;\r
5562 \r
5563   switch (ms->name[0]) {\r
5564   case NULLCHAR:\r
5565     /* Silence */\r
5566     ok = TRUE;\r
5567     break;\r
5568   case '$':\r
5569     /* System sound from Control Panel.  Don't preload here. */\r
5570     ok = TRUE;\r
5571     break;\r
5572   case '!':\r
5573     if (ms->name[1] == NULLCHAR) {\r
5574       /* "!" alone = silence */\r
5575       ok = TRUE;\r
5576     } else {\r
5577       /* Builtin wave resource.  Error if not found. */\r
5578       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5579       if (h == NULL) break;\r
5580       ms->data = (void *)LoadResource(hInst, h);\r
5581       if (h == NULL) break;\r
5582       ok = TRUE;\r
5583     }\r
5584     break;\r
5585   default:\r
5586     /* .wav file.  Error if not found. */\r
5587     f = fopen(ms->name, "rb");\r
5588     if (f == NULL) break;\r
5589     if (fstat(fileno(f), &st) < 0) break;\r
5590     ms->data = malloc(st.st_size);\r
5591     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5592     fclose(f);\r
5593     ok = TRUE;\r
5594     break;\r
5595   }\r
5596   if (!ok) {\r
5597     char buf[MSG_SIZ];\r
5598       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5599     DisplayError(buf, GetLastError());\r
5600   }\r
5601   return ok;\r
5602 }\r
5603 \r
5604 BOOLEAN\r
5605 MyPlaySound(MySound *ms)\r
5606 {\r
5607   BOOLEAN ok = FALSE;\r
5608 \r
5609   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5610   switch (ms->name[0]) {\r
5611   case NULLCHAR:\r
5612         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5613     /* Silence */\r
5614     ok = TRUE;\r
5615     break;\r
5616   case '$':\r
5617     /* System sound from Control Panel (deprecated feature).\r
5618        "$" alone or an unset sound name gets default beep (still in use). */\r
5619     if (ms->name[1]) {\r
5620       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5621     }\r
5622     if (!ok) ok = MessageBeep(MB_OK);\r
5623     break; \r
5624   case '!':\r
5625     /* Builtin wave resource, or "!" alone for silence */\r
5626     if (ms->name[1]) {\r
5627       if (ms->data == NULL) return FALSE;\r
5628       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5629     } else {\r
5630       ok = TRUE;\r
5631     }\r
5632     break;\r
5633   default:\r
5634     /* .wav file.  Error if not found. */\r
5635     if (ms->data == NULL) return FALSE;\r
5636     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5637     break;\r
5638   }\r
5639   /* Don't print an error: this can happen innocently if the sound driver\r
5640      is busy; for instance, if another instance of WinBoard is playing\r
5641      a sound at about the same time. */\r
5642   return ok;\r
5643 }\r
5644 \r
5645 \r
5646 LRESULT CALLBACK\r
5647 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5648 {\r
5649   BOOL ok;\r
5650   OPENFILENAME *ofn;\r
5651   static UINT *number; /* gross that this is static */\r
5652 \r
5653   switch (message) {\r
5654   case WM_INITDIALOG: /* message: initialize dialog box */\r
5655     /* Center the dialog over the application window */\r
5656     ofn = (OPENFILENAME *) lParam;\r
5657     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5658       number = (UINT *) ofn->lCustData;\r
5659       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5660     } else {\r
5661       number = NULL;\r
5662     }\r
5663     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5664     Translate(hDlg, 1536);\r
5665     return FALSE;  /* Allow for further processing */\r
5666 \r
5667   case WM_COMMAND:\r
5668     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5669       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5670     }\r
5671     return FALSE;  /* Allow for further processing */\r
5672   }\r
5673   return FALSE;\r
5674 }\r
5675 \r
5676 UINT APIENTRY\r
5677 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5678 {\r
5679   static UINT *number;\r
5680   OPENFILENAME *ofname;\r
5681   OFNOTIFY *ofnot;\r
5682   switch (uiMsg) {\r
5683   case WM_INITDIALOG:\r
5684     Translate(hdlg, DLG_IndexNumber);\r
5685     ofname = (OPENFILENAME *)lParam;\r
5686     number = (UINT *)(ofname->lCustData);\r
5687     break;\r
5688   case WM_NOTIFY:\r
5689     ofnot = (OFNOTIFY *)lParam;\r
5690     if (ofnot->hdr.code == CDN_FILEOK) {\r
5691       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5692     }\r
5693     break;\r
5694   }\r
5695   return 0;\r
5696 }\r
5697 \r
5698 \r
5699 FILE *\r
5700 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5701                char *nameFilt, char *dlgTitle, UINT *number,\r
5702                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5703 {\r
5704   OPENFILENAME openFileName;\r
5705   char buf1[MSG_SIZ];\r
5706   FILE *f;\r
5707 \r
5708   if (fileName == NULL) fileName = buf1;\r
5709   if (defName == NULL) {\r
5710     safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );\r
5711     strcat(fileName, defExt);\r
5712   } else {\r
5713     safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );\r
5714   }\r
5715     if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );\r
5716   if (number) *number = 0;\r
5717 \r
5718   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5719   openFileName.hwndOwner         = hwnd;\r
5720   openFileName.hInstance         = (HANDLE) hInst;\r
5721   openFileName.lpstrFilter       = nameFilt;\r
5722   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5723   openFileName.nMaxCustFilter    = 0L;\r
5724   openFileName.nFilterIndex      = 1L;\r
5725   openFileName.lpstrFile         = fileName;\r
5726   openFileName.nMaxFile          = MSG_SIZ;\r
5727   openFileName.lpstrFileTitle    = fileTitle;\r
5728   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5729   openFileName.lpstrInitialDir   = NULL;\r
5730   openFileName.lpstrTitle        = dlgTitle;\r
5731   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5732     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5733     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5734     | (oldDialog ? 0 : OFN_EXPLORER);\r
5735   openFileName.nFileOffset       = 0;\r
5736   openFileName.nFileExtension    = 0;\r
5737   openFileName.lpstrDefExt       = defExt;\r
5738   openFileName.lCustData         = (LONG) number;\r
5739   openFileName.lpfnHook          = oldDialog ?\r
5740     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5741   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5742 \r
5743   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5744                         GetOpenFileName(&openFileName)) {\r
5745     /* open the file */\r
5746     f = fopen(openFileName.lpstrFile, write);\r
5747     if (f == NULL) {\r
5748       MessageBox(hwnd, _("File open failed"), NULL,\r
5749                  MB_OK|MB_ICONEXCLAMATION);\r
5750       return NULL;\r
5751     }\r
5752   } else {\r
5753     int err = CommDlgExtendedError();\r
5754     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5755     return FALSE;\r
5756   }\r
5757   return f;\r
5758 }\r
5759 \r
5760 \r
5761 \r
5762 VOID APIENTRY\r
5763 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5764 {\r
5765   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5766 \r
5767   /*\r
5768    * Get the first pop-up menu in the menu template. This is the\r
5769    * menu that TrackPopupMenu displays.\r
5770    */\r
5771   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5772 \r
5773   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5774 \r
5775   /*\r
5776    * TrackPopup uses screen coordinates, so convert the\r
5777    * coordinates of the mouse click to screen coordinates.\r
5778    */\r
5779   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5780 \r
5781   /* Draw and track the floating pop-up menu. */\r
5782   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5783                  pt.x, pt.y, 0, hwnd, NULL);\r
5784 \r
5785   /* Destroy the menu.*/\r
5786   DestroyMenu(hmenu);\r
5787 }\r
5788    \r
5789 typedef struct {\r
5790   HWND hDlg, hText;\r
5791   int sizeX, sizeY, newSizeX, newSizeY;\r
5792   HDWP hdwp;\r
5793 } ResizeEditPlusButtonsClosure;\r
5794 \r
5795 BOOL CALLBACK\r
5796 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5797 {\r
5798   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5799   RECT rect;\r
5800   POINT pt;\r
5801 \r
5802   if (hChild == cl->hText) return TRUE;\r
5803   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5804   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5805   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5806   ScreenToClient(cl->hDlg, &pt);\r
5807   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5808     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5809   return TRUE;\r
5810 }\r
5811 \r
5812 /* Resize a dialog that has a (rich) edit field filling most of\r
5813    the top, with a row of buttons below */\r
5814 VOID\r
5815 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5816 {\r
5817   RECT rectText;\r
5818   int newTextHeight, newTextWidth;\r
5819   ResizeEditPlusButtonsClosure cl;\r
5820   \r
5821   /*if (IsIconic(hDlg)) return;*/\r
5822   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5823   \r
5824   cl.hdwp = BeginDeferWindowPos(8);\r
5825 \r
5826   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5827   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5828   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5829   if (newTextHeight < 0) {\r
5830     newSizeY += -newTextHeight;\r
5831     newTextHeight = 0;\r
5832   }\r
5833   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5834     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5835 \r
5836   cl.hDlg = hDlg;\r
5837   cl.hText = hText;\r
5838   cl.sizeX = sizeX;\r
5839   cl.sizeY = sizeY;\r
5840   cl.newSizeX = newSizeX;\r
5841   cl.newSizeY = newSizeY;\r
5842   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5843 \r
5844   EndDeferWindowPos(cl.hdwp);\r
5845 }\r
5846 \r
5847 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5848 {\r
5849     RECT    rChild, rParent;\r
5850     int     wChild, hChild, wParent, hParent;\r
5851     int     wScreen, hScreen, xNew, yNew;\r
5852     HDC     hdc;\r
5853 \r
5854     /* Get the Height and Width of the child window */\r
5855     GetWindowRect (hwndChild, &rChild);\r
5856     wChild = rChild.right - rChild.left;\r
5857     hChild = rChild.bottom - rChild.top;\r
5858 \r
5859     /* Get the Height and Width of the parent window */\r
5860     GetWindowRect (hwndParent, &rParent);\r
5861     wParent = rParent.right - rParent.left;\r
5862     hParent = rParent.bottom - rParent.top;\r
5863 \r
5864     /* Get the display limits */\r
5865     hdc = GetDC (hwndChild);\r
5866     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5867     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5868     ReleaseDC(hwndChild, hdc);\r
5869 \r
5870     /* Calculate new X position, then adjust for screen */\r
5871     xNew = rParent.left + ((wParent - wChild) /2);\r
5872     if (xNew < 0) {\r
5873         xNew = 0;\r
5874     } else if ((xNew+wChild) > wScreen) {\r
5875         xNew = wScreen - wChild;\r
5876     }\r
5877 \r
5878     /* Calculate new Y position, then adjust for screen */\r
5879     if( mode == 0 ) {\r
5880         yNew = rParent.top  + ((hParent - hChild) /2);\r
5881     }\r
5882     else {\r
5883         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5884     }\r
5885 \r
5886     if (yNew < 0) {\r
5887         yNew = 0;\r
5888     } else if ((yNew+hChild) > hScreen) {\r
5889         yNew = hScreen - hChild;\r
5890     }\r
5891 \r
5892     /* Set it, and return */\r
5893     return SetWindowPos (hwndChild, NULL,\r
5894                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5895 }\r
5896 \r
5897 /* Center one window over another */\r
5898 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5899 {\r
5900     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5901 }\r
5902 \r
5903 /*---------------------------------------------------------------------------*\\r
5904  *\r
5905  * Startup Dialog functions\r
5906  *\r
5907 \*---------------------------------------------------------------------------*/\r
5908 void\r
5909 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5910 {\r
5911   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5912 \r
5913   while (*cd != NULL) {\r
5914     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5915     cd++;\r
5916   }\r
5917 }\r
5918 \r
5919 void\r
5920 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5921 {\r
5922   char buf1[MAX_ARG_LEN];\r
5923   int len;\r
5924 \r
5925   if (str[0] == '@') {\r
5926     FILE* f = fopen(str + 1, "r");\r
5927     if (f == NULL) {\r
5928       DisplayFatalError(str + 1, errno, 2);\r
5929       return;\r
5930     }\r
5931     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5932     fclose(f);\r
5933     buf1[len] = NULLCHAR;\r
5934     str = buf1;\r
5935   }\r
5936 \r
5937   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5938 \r
5939   for (;;) {\r
5940     char buf[MSG_SIZ];\r
5941     char *end = strchr(str, '\n');\r
5942     if (end == NULL) return;\r
5943     memcpy(buf, str, end - str);\r
5944     buf[end - str] = NULLCHAR;\r
5945     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5946     str = end + 1;\r
5947   }\r
5948 }\r
5949 \r
5950 void\r
5951 SetStartupDialogEnables(HWND hDlg)\r
5952 {\r
5953   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5954     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5955     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5956   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5957     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5958   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5959     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5960   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5961     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5962   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5963     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5964     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5965     IsDlgButtonChecked(hDlg, OPT_View));\r
5966 }\r
5967 \r
5968 char *\r
5969 QuoteForFilename(char *filename)\r
5970 {\r
5971   int dquote, space;\r
5972   dquote = strchr(filename, '"') != NULL;\r
5973   space = strchr(filename, ' ') != NULL;\r
5974   if (dquote || space) {\r
5975     if (dquote) {\r
5976       return "'";\r
5977     } else {\r
5978       return "\"";\r
5979     }\r
5980   } else {\r
5981     return "";\r
5982   }\r
5983 }\r
5984 \r
5985 VOID\r
5986 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5987 {\r
5988   char buf[MSG_SIZ];\r
5989   char *q;\r
5990 \r
5991   InitComboStringsFromOption(hwndCombo, nthnames);\r
5992   q = QuoteForFilename(nthcp);\r
5993     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
5994   if (*nthdir != NULLCHAR) {\r
5995     q = QuoteForFilename(nthdir);\r
5996       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
5997   }\r
5998   if (*nthcp == NULLCHAR) {\r
5999     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6000   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6001     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6002     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6003   }\r
6004 }\r
6005 \r
6006 LRESULT CALLBACK\r
6007 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6008 {\r
6009   char buf[MSG_SIZ];\r
6010   HANDLE hwndCombo;\r
6011   char *p;\r
6012 \r
6013   switch (message) {\r
6014   case WM_INITDIALOG:\r
6015     /* Center the dialog */\r
6016     CenterWindow (hDlg, GetDesktopWindow());\r
6017     Translate(hDlg, DLG_Startup);\r
6018     /* Initialize the dialog items */\r
6019     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6020                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6021                   firstChessProgramNames);\r
6022     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6023                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6024                   secondChessProgramNames);\r
6025     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6026     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6027       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6028     if (*appData.icsHelper != NULLCHAR) {\r
6029       char *q = QuoteForFilename(appData.icsHelper);\r
6030       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6031     }\r
6032     if (*appData.icsHost == NULLCHAR) {\r
6033       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6034       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6035     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6036       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6037       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6038     }\r
6039 \r
6040     if (appData.icsActive) {\r
6041       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6042     }\r
6043     else if (appData.noChessProgram) {\r
6044       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6045     }\r
6046     else {\r
6047       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6048     }\r
6049 \r
6050     SetStartupDialogEnables(hDlg);\r
6051     return TRUE;\r
6052 \r
6053   case WM_COMMAND:\r
6054     switch (LOWORD(wParam)) {\r
6055     case IDOK:\r
6056       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6057         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6058         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6059         p = buf;\r
6060         ParseArgs(StringGet, &p);\r
6061         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6062         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6063         p = buf;\r
6064         ParseArgs(StringGet, &p);\r
6065         appData.noChessProgram = FALSE;\r
6066         appData.icsActive = FALSE;\r
6067       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6068         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6069         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6070         p = buf;\r
6071         ParseArgs(StringGet, &p);\r
6072         if (appData.zippyPlay) {\r
6073           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6074           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6075           p = buf;\r
6076           ParseArgs(StringGet, &p);\r
6077         }\r
6078       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6079         appData.noChessProgram = TRUE;\r
6080         appData.icsActive = FALSE;\r
6081       } else {\r
6082         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6083                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6084         return TRUE;\r
6085       }\r
6086       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6087         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6088         p = buf;\r
6089         ParseArgs(StringGet, &p);\r
6090       }\r
6091       EndDialog(hDlg, TRUE);\r
6092       return TRUE;\r
6093 \r
6094     case IDCANCEL:\r
6095       ExitEvent(0);\r
6096       return TRUE;\r
6097 \r
6098     case IDM_HELPCONTENTS:\r
6099       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6100         MessageBox (GetFocus(),\r
6101                     _("Unable to activate help"),\r
6102                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6103       }\r
6104       break;\r
6105 \r
6106     default:\r
6107       SetStartupDialogEnables(hDlg);\r
6108       break;\r
6109     }\r
6110     break;\r
6111   }\r
6112   return FALSE;\r
6113 }\r
6114 \r
6115 /*---------------------------------------------------------------------------*\\r
6116  *\r
6117  * About box dialog functions\r
6118  *\r
6119 \*---------------------------------------------------------------------------*/\r
6120 \r
6121 /* Process messages for "About" dialog box */\r
6122 LRESULT CALLBACK\r
6123 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6124 {\r
6125   switch (message) {\r
6126   case WM_INITDIALOG: /* message: initialize dialog box */\r
6127     /* Center the dialog over the application window */\r
6128     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6129     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6130     Translate(hDlg, ABOUTBOX);\r
6131     JAWS_COPYRIGHT\r
6132     return (TRUE);\r
6133 \r
6134   case WM_COMMAND: /* message: received a command */\r
6135     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6136         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6137       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6138       return (TRUE);\r
6139     }\r
6140     break;\r
6141   }\r
6142   return (FALSE);\r
6143 }\r
6144 \r
6145 /*---------------------------------------------------------------------------*\\r
6146  *\r
6147  * Comment Dialog functions\r
6148  *\r
6149 \*---------------------------------------------------------------------------*/\r
6150 \r
6151 LRESULT CALLBACK\r
6152 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6153 {\r
6154   static HANDLE hwndText = NULL;\r
6155   int len, newSizeX, newSizeY, flags;\r
6156   static int sizeX, sizeY;\r
6157   char *str;\r
6158   RECT rect;\r
6159   MINMAXINFO *mmi;\r
6160 \r
6161   switch (message) {\r
6162   case WM_INITDIALOG: /* message: initialize dialog box */\r
6163     /* Initialize the dialog items */\r
6164     Translate(hDlg, DLG_EditComment);\r
6165     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6166     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6167     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6168     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6169     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6170     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6171     SetWindowText(hDlg, commentTitle);\r
6172     if (editComment) {\r
6173       SetFocus(hwndText);\r
6174     } else {\r
6175       SetFocus(GetDlgItem(hDlg, IDOK));\r
6176     }\r
6177     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6178                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6179                 MAKELPARAM(FALSE, 0));\r
6180     /* Size and position the dialog */\r
6181     if (!commentDialog) {\r
6182       commentDialog = hDlg;\r
6183       flags = SWP_NOZORDER;\r
6184       GetClientRect(hDlg, &rect);\r
6185       sizeX = rect.right;\r
6186       sizeY = rect.bottom;\r
6187       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6188           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6189         WINDOWPLACEMENT wp;\r
6190         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6191         wp.length = sizeof(WINDOWPLACEMENT);\r
6192         wp.flags = 0;\r
6193         wp.showCmd = SW_SHOW;\r
6194         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6195         wp.rcNormalPosition.left = wpComment.x;\r
6196         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6197         wp.rcNormalPosition.top = wpComment.y;\r
6198         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6199         SetWindowPlacement(hDlg, &wp);\r
6200 \r
6201         GetClientRect(hDlg, &rect);\r
6202         newSizeX = rect.right;\r
6203         newSizeY = rect.bottom;\r
6204         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6205                               newSizeX, newSizeY);\r
6206         sizeX = newSizeX;\r
6207         sizeY = newSizeY;\r
6208       }\r
6209     }\r
6210     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6211     return FALSE;\r
6212 \r
6213   case WM_COMMAND: /* message: received a command */\r
6214     switch (LOWORD(wParam)) {\r
6215     case IDOK:\r
6216       if (editComment) {\r
6217         char *p, *q;\r
6218         /* Read changed options from the dialog box */\r
6219         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6220         len = GetWindowTextLength(hwndText);\r
6221         str = (char *) malloc(len + 1);\r
6222         GetWindowText(hwndText, str, len + 1);\r
6223         p = q = str;\r
6224         while (*q) {\r
6225           if (*q == '\r')\r
6226             q++;\r
6227           else\r
6228             *p++ = *q++;\r
6229         }\r
6230         *p = NULLCHAR;\r
6231         ReplaceComment(commentIndex, str);\r
6232         free(str);\r
6233       }\r
6234       CommentPopDown();\r
6235       return TRUE;\r
6236 \r
6237     case IDCANCEL:\r
6238     case OPT_CancelComment:\r
6239       CommentPopDown();\r
6240       return TRUE;\r
6241 \r
6242     case OPT_ClearComment:\r
6243       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6244       break;\r
6245 \r
6246     case OPT_EditComment:\r
6247       EditCommentEvent();\r
6248       return TRUE;\r
6249 \r
6250     default:\r
6251       break;\r
6252     }\r
6253     break;\r
6254 \r
6255   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6256         if( wParam == OPT_CommentText ) {\r
6257             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6258 \r
6259             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6260                 POINTL pt;\r
6261                 LRESULT index;\r
6262 \r
6263                 pt.x = LOWORD( lpMF->lParam );\r
6264                 pt.y = HIWORD( lpMF->lParam );\r
6265 \r
6266                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6267 \r
6268                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6269                 len = GetWindowTextLength(hwndText);\r
6270                 str = (char *) malloc(len + 1);\r
6271                 GetWindowText(hwndText, str, len + 1);\r
6272                 ReplaceComment(commentIndex, str);\r
6273                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6274                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6275                 free(str);\r
6276 \r
6277                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6278                 lpMF->msg = WM_USER;\r
6279 \r
6280                 return TRUE;\r
6281             }\r
6282         }\r
6283         break;\r
6284 \r
6285   case WM_SIZE:\r
6286     newSizeX = LOWORD(lParam);\r
6287     newSizeY = HIWORD(lParam);\r
6288     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6289     sizeX = newSizeX;\r
6290     sizeY = newSizeY;\r
6291     break;\r
6292 \r
6293   case WM_GETMINMAXINFO:\r
6294     /* Prevent resizing window too small */\r
6295     mmi = (MINMAXINFO *) lParam;\r
6296     mmi->ptMinTrackSize.x = 100;\r
6297     mmi->ptMinTrackSize.y = 100;\r
6298     break;\r
6299   }\r
6300   return FALSE;\r
6301 }\r
6302 \r
6303 VOID\r
6304 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6305 {\r
6306   FARPROC lpProc;\r
6307   char *p, *q;\r
6308 \r
6309   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6310 \r
6311   if (str == NULL) str = "";\r
6312   p = (char *) malloc(2 * strlen(str) + 2);\r
6313   q = p;\r
6314   while (*str) {\r
6315     if (*str == '\n') *q++ = '\r';\r
6316     *q++ = *str++;\r
6317   }\r
6318   *q = NULLCHAR;\r
6319   if (commentText != NULL) free(commentText);\r
6320 \r
6321   commentIndex = index;\r
6322   commentTitle = title;\r
6323   commentText = p;\r
6324   editComment = edit;\r
6325 \r
6326   if (commentDialog) {\r
6327     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6328     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6329   } else {\r
6330     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6331     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6332                  hwndMain, (DLGPROC)lpProc);\r
6333     FreeProcInstance(lpProc);\r
6334   }\r
6335   commentUp = TRUE;\r
6336 }\r
6337 \r
6338 \r
6339 /*---------------------------------------------------------------------------*\\r
6340  *\r
6341  * Type-in move dialog functions\r
6342  * \r
6343 \*---------------------------------------------------------------------------*/\r
6344 \r
6345 LRESULT CALLBACK\r
6346 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6347 {\r
6348   char move[MSG_SIZ];\r
6349   HWND hInput;\r
6350   ChessMove moveType;\r
6351   int fromX, fromY, toX, toY;\r
6352   char promoChar;\r
6353 \r
6354   switch (message) {\r
6355   case WM_INITDIALOG:\r
6356     move[0] = (char) lParam;\r
6357     move[1] = NULLCHAR;\r
6358     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6359     Translate(hDlg, DLG_TypeInMove);\r
6360     hInput = GetDlgItem(hDlg, OPT_Move);\r
6361     SetWindowText(hInput, move);\r
6362     SetFocus(hInput);\r
6363     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6364     return FALSE;\r
6365 \r
6366   case WM_COMMAND:\r
6367     switch (LOWORD(wParam)) {\r
6368     case IDOK:\r
6369       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6370       { int n; Board board;\r
6371         // [HGM] FENedit\r
6372         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6373                 EditPositionPasteFEN(move);\r
6374                 EndDialog(hDlg, TRUE);\r
6375                 return TRUE;\r
6376         }\r
6377         // [HGM] movenum: allow move number to be typed in any mode\r
6378         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6379           ToNrEvent(2*n-1);\r
6380           EndDialog(hDlg, TRUE);\r
6381           return TRUE;\r
6382         }\r
6383       }\r
6384       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6385         gameMode != Training) {\r
6386         DisplayMoveError(_("Displayed move is not current"));\r
6387       } else {\r
6388 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6389         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6390           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6391         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6392         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6393           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6394           if (gameMode != Training)\r
6395               forwardMostMove = currentMove;\r
6396           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6397         } else {\r
6398           DisplayMoveError(_("Could not parse move"));\r
6399         }\r
6400       }\r
6401       EndDialog(hDlg, TRUE);\r
6402       return TRUE;\r
6403     case IDCANCEL:\r
6404       EndDialog(hDlg, FALSE);\r
6405       return TRUE;\r
6406     default:\r
6407       break;\r
6408     }\r
6409     break;\r
6410   }\r
6411   return FALSE;\r
6412 }\r
6413 \r
6414 VOID\r
6415 PopUpMoveDialog(char firstchar)\r
6416 {\r
6417     FARPROC lpProc;\r
6418     \r
6419     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6420         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6421         gameMode == AnalyzeMode || gameMode == EditGame || \r
6422         gameMode == EditPosition || gameMode == IcsExamining ||\r
6423         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6424         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6425                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6426                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6427         gameMode == Training) {\r
6428       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6429       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6430         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6431       FreeProcInstance(lpProc);\r
6432     }\r
6433 }\r
6434 \r
6435 /*---------------------------------------------------------------------------*\\r
6436  *\r
6437  * Type-in name dialog functions\r
6438  * \r
6439 \*---------------------------------------------------------------------------*/\r
6440 \r
6441 LRESULT CALLBACK\r
6442 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6443 {\r
6444   char move[MSG_SIZ];\r
6445   HWND hInput;\r
6446 \r
6447   switch (message) {\r
6448   case WM_INITDIALOG:\r
6449     move[0] = (char) lParam;\r
6450     move[1] = NULLCHAR;\r
6451     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6452     Translate(hDlg, DLG_TypeInName);\r
6453     hInput = GetDlgItem(hDlg, OPT_Name);\r
6454     SetWindowText(hInput, move);\r
6455     SetFocus(hInput);\r
6456     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6457     return FALSE;\r
6458 \r
6459   case WM_COMMAND:\r
6460     switch (LOWORD(wParam)) {\r
6461     case IDOK:\r
6462       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6463       appData.userName = strdup(move);\r
6464       SetUserLogo();\r
6465       SetGameInfo();\r
6466       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6467         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6468         DisplayTitle(move);\r
6469       }\r
6470 \r
6471 \r
6472       EndDialog(hDlg, TRUE);\r
6473       return TRUE;\r
6474     case IDCANCEL:\r
6475       EndDialog(hDlg, FALSE);\r
6476       return TRUE;\r
6477     default:\r
6478       break;\r
6479     }\r
6480     break;\r
6481   }\r
6482   return FALSE;\r
6483 }\r
6484 \r
6485 VOID\r
6486 PopUpNameDialog(char firstchar)\r
6487 {\r
6488     FARPROC lpProc;\r
6489     \r
6490       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6491       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6492         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6493       FreeProcInstance(lpProc);\r
6494 }\r
6495 \r
6496 /*---------------------------------------------------------------------------*\\r
6497  *\r
6498  *  Error dialogs\r
6499  * \r
6500 \*---------------------------------------------------------------------------*/\r
6501 \r
6502 /* Nonmodal error box */\r
6503 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6504                              WPARAM wParam, LPARAM lParam);\r
6505 \r
6506 VOID\r
6507 ErrorPopUp(char *title, char *content)\r
6508 {\r
6509   FARPROC lpProc;\r
6510   char *p, *q;\r
6511   BOOLEAN modal = hwndMain == NULL;\r
6512 \r
6513   p = content;\r
6514   q = errorMessage;\r
6515   while (*p) {\r
6516     if (*p == '\n') {\r
6517       if (modal) {\r
6518         *q++ = ' ';\r
6519         p++;\r
6520       } else {\r
6521         *q++ = '\r';\r
6522         *q++ = *p++;\r
6523       }\r
6524     } else {\r
6525       *q++ = *p++;\r
6526     }\r
6527   }\r
6528   *q = NULLCHAR;\r
6529   strncpy(errorTitle, title, sizeof(errorTitle));\r
6530   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6531   \r
6532   if (modal) {\r
6533     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6534   } else {\r
6535     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6536     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6537                  hwndMain, (DLGPROC)lpProc);\r
6538     FreeProcInstance(lpProc);\r
6539   }\r
6540 }\r
6541 \r
6542 VOID\r
6543 ErrorPopDown()\r
6544 {\r
6545   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6546   if (errorDialog == NULL) return;\r
6547   DestroyWindow(errorDialog);\r
6548   errorDialog = NULL;\r
6549   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6550 }\r
6551 \r
6552 LRESULT CALLBACK\r
6553 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6554 {\r
6555   HANDLE hwndText;\r
6556   RECT rChild;\r
6557 \r
6558   switch (message) {\r
6559   case WM_INITDIALOG:\r
6560     GetWindowRect(hDlg, &rChild);\r
6561 \r
6562     /*\r
6563     SetWindowPos(hDlg, NULL, rChild.left,\r
6564       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6565       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6566     */\r
6567 \r
6568     /* \r
6569         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6570         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6571         and it doesn't work when you resize the dialog.\r
6572         For now, just give it a default position.\r
6573     */\r
6574     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6575     Translate(hDlg, DLG_Error);\r
6576 \r
6577     errorDialog = hDlg;\r
6578     SetWindowText(hDlg, errorTitle);\r
6579     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6580     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6581     return FALSE;\r
6582 \r
6583   case WM_COMMAND:\r
6584     switch (LOWORD(wParam)) {\r
6585     case IDOK:\r
6586     case IDCANCEL:\r
6587       if (errorDialog == hDlg) errorDialog = NULL;\r
6588       DestroyWindow(hDlg);\r
6589       return TRUE;\r
6590 \r
6591     default:\r
6592       break;\r
6593     }\r
6594     break;\r
6595   }\r
6596   return FALSE;\r
6597 }\r
6598 \r
6599 #ifdef GOTHIC\r
6600 HWND gothicDialog = NULL;\r
6601 \r
6602 LRESULT CALLBACK\r
6603 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6604 {\r
6605   HANDLE hwndText;\r
6606   RECT rChild;\r
6607   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6608 \r
6609   switch (message) {\r
6610   case WM_INITDIALOG:\r
6611     GetWindowRect(hDlg, &rChild);\r
6612 \r
6613     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6614                                                              SWP_NOZORDER);\r
6615 \r
6616     /* \r
6617         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6618         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6619         and it doesn't work when you resize the dialog.\r
6620         For now, just give it a default position.\r
6621     */\r
6622     gothicDialog = hDlg;\r
6623     SetWindowText(hDlg, errorTitle);\r
6624     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6625     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6626     return FALSE;\r
6627 \r
6628   case WM_COMMAND:\r
6629     switch (LOWORD(wParam)) {\r
6630     case IDOK:\r
6631     case IDCANCEL:\r
6632       if (errorDialog == hDlg) errorDialog = NULL;\r
6633       DestroyWindow(hDlg);\r
6634       return TRUE;\r
6635 \r
6636     default:\r
6637       break;\r
6638     }\r
6639     break;\r
6640   }\r
6641   return FALSE;\r
6642 }\r
6643 \r
6644 VOID\r
6645 GothicPopUp(char *title, VariantClass variant)\r
6646 {\r
6647   FARPROC lpProc;\r
6648   static char *lastTitle;\r
6649 \r
6650   strncpy(errorTitle, title, sizeof(errorTitle));\r
6651   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6652 \r
6653   if(lastTitle != title && gothicDialog != NULL) {\r
6654     DestroyWindow(gothicDialog);\r
6655     gothicDialog = NULL;\r
6656   }\r
6657   if(variant != VariantNormal && gothicDialog == NULL) {\r
6658     title = lastTitle;\r
6659     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6660     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6661                  hwndMain, (DLGPROC)lpProc);\r
6662     FreeProcInstance(lpProc);\r
6663   }\r
6664 }\r
6665 #endif\r
6666 \r
6667 /*---------------------------------------------------------------------------*\\r
6668  *\r
6669  *  Ics Interaction console functions\r
6670  *\r
6671 \*---------------------------------------------------------------------------*/\r
6672 \r
6673 #define HISTORY_SIZE 64\r
6674 static char *history[HISTORY_SIZE];\r
6675 int histIn = 0, histP = 0;\r
6676 \r
6677 VOID\r
6678 SaveInHistory(char *cmd)\r
6679 {\r
6680   if (history[histIn] != NULL) {\r
6681     free(history[histIn]);\r
6682     history[histIn] = NULL;\r
6683   }\r
6684   if (*cmd == NULLCHAR) return;\r
6685   history[histIn] = StrSave(cmd);\r
6686   histIn = (histIn + 1) % HISTORY_SIZE;\r
6687   if (history[histIn] != NULL) {\r
6688     free(history[histIn]);\r
6689     history[histIn] = NULL;\r
6690   }\r
6691   histP = histIn;\r
6692 }\r
6693 \r
6694 char *\r
6695 PrevInHistory(char *cmd)\r
6696 {\r
6697   int newhp;\r
6698   if (histP == histIn) {\r
6699     if (history[histIn] != NULL) free(history[histIn]);\r
6700     history[histIn] = StrSave(cmd);\r
6701   }\r
6702   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6703   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6704   histP = newhp;\r
6705   return history[histP];\r
6706 }\r
6707 \r
6708 char *\r
6709 NextInHistory()\r
6710 {\r
6711   if (histP == histIn) return NULL;\r
6712   histP = (histP + 1) % HISTORY_SIZE;\r
6713   return history[histP];   \r
6714 }\r
6715 \r
6716 HMENU\r
6717 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6718 {\r
6719   HMENU hmenu, h;\r
6720   int i = 0;\r
6721   hmenu = LoadMenu(hInst, "TextMenu");\r
6722   h = GetSubMenu(hmenu, 0);\r
6723   while (e->item) {\r
6724     if (strcmp(e->item, "-") == 0) {\r
6725       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6726     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6727       int flags = MF_STRING, j = 0;\r
6728       if (e->item[0] == '|') {\r
6729         flags |= MF_MENUBARBREAK;\r
6730         j++;\r
6731       }\r
6732       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6733       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6734     }\r
6735     e++;\r
6736     i++;\r
6737   } \r
6738   return hmenu;\r
6739 }\r
6740 \r
6741 WNDPROC consoleTextWindowProc;\r
6742 \r
6743 void\r
6744 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6745 {\r
6746   char buf[MSG_SIZ], name[MSG_SIZ];\r
6747   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6748   CHARRANGE sel;\r
6749 \r
6750   if (!getname) {\r
6751     SetWindowText(hInput, command);\r
6752     if (immediate) {\r
6753       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6754     } else {\r
6755       sel.cpMin = 999999;\r
6756       sel.cpMax = 999999;\r
6757       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6758       SetFocus(hInput);\r
6759     }\r
6760     return;\r
6761   }    \r
6762   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6763   if (sel.cpMin == sel.cpMax) {\r
6764     /* Expand to surrounding word */\r
6765     TEXTRANGE tr;\r
6766     do {\r
6767       tr.chrg.cpMax = sel.cpMin;\r
6768       tr.chrg.cpMin = --sel.cpMin;\r
6769       if (sel.cpMin < 0) break;\r
6770       tr.lpstrText = name;\r
6771       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6772     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6773     sel.cpMin++;\r
6774 \r
6775     do {\r
6776       tr.chrg.cpMin = sel.cpMax;\r
6777       tr.chrg.cpMax = ++sel.cpMax;\r
6778       tr.lpstrText = name;\r
6779       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6780     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6781     sel.cpMax--;\r
6782 \r
6783     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6784       MessageBeep(MB_ICONEXCLAMATION);\r
6785       return;\r
6786     }\r
6787     tr.chrg = sel;\r
6788     tr.lpstrText = name;\r
6789     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6790   } else {\r
6791     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6792       MessageBeep(MB_ICONEXCLAMATION);\r
6793       return;\r
6794     }\r
6795     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6796   }\r
6797   if (immediate) {\r
6798     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6799     SetWindowText(hInput, buf);\r
6800     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6801   } else {\r
6802     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6803       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6804     SetWindowText(hInput, buf);\r
6805     sel.cpMin = 999999;\r
6806     sel.cpMax = 999999;\r
6807     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6808     SetFocus(hInput);\r
6809   }\r
6810 }\r
6811 \r
6812 LRESULT CALLBACK \r
6813 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6814 {\r
6815   HWND hInput;\r
6816   CHARRANGE sel;\r
6817 \r
6818   switch (message) {\r
6819   case WM_KEYDOWN:\r
6820     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6821     switch (wParam) {\r
6822     case VK_PRIOR:\r
6823       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6824       return 0;\r
6825     case VK_NEXT:\r
6826       sel.cpMin = 999999;\r
6827       sel.cpMax = 999999;\r
6828       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6829       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6830       return 0;\r
6831     }\r
6832     break;\r
6833   case WM_CHAR:\r
6834    if(wParam != '\022') {\r
6835     if (wParam == '\t') {\r
6836       if (GetKeyState(VK_SHIFT) < 0) {\r
6837         /* shifted */\r
6838         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6839         if (buttonDesc[0].hwnd) {\r
6840           SetFocus(buttonDesc[0].hwnd);\r
6841         } else {\r
6842           SetFocus(hwndMain);\r
6843         }\r
6844       } else {\r
6845         /* unshifted */\r
6846         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6847       }\r
6848     } else {\r
6849       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6850       JAWS_DELETE( SetFocus(hInput); )\r
6851       SendMessage(hInput, message, wParam, lParam);\r
6852     }\r
6853     return 0;\r
6854    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6855   case WM_RBUTTONDOWN:\r
6856     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6857       /* Move selection here if it was empty */\r
6858       POINT pt;\r
6859       pt.x = LOWORD(lParam);\r
6860       pt.y = HIWORD(lParam);\r
6861       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6862       if (sel.cpMin == sel.cpMax) {\r
6863         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6864         sel.cpMax = sel.cpMin;\r
6865         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6866       }\r
6867       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6868 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6869       POINT pt;\r
6870       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6871       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6872       if (sel.cpMin == sel.cpMax) {\r
6873         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6874         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6875       }\r
6876       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6877         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6878       }\r
6879       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6880       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6881       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6882       MenuPopup(hwnd, pt, hmenu, -1);\r
6883 }\r
6884     }\r
6885     return 0;\r
6886   case WM_RBUTTONUP:\r
6887     if (GetKeyState(VK_SHIFT) & ~1) {\r
6888       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6889         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6890     }\r
6891     return 0;\r
6892   case WM_PASTE:\r
6893     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6894     SetFocus(hInput);\r
6895     return SendMessage(hInput, message, wParam, lParam);\r
6896   case WM_MBUTTONDOWN:\r
6897     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6898   case WM_COMMAND:\r
6899     switch (LOWORD(wParam)) {\r
6900     case IDM_QuickPaste:\r
6901       {\r
6902         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6903         if (sel.cpMin == sel.cpMax) {\r
6904           MessageBeep(MB_ICONEXCLAMATION);\r
6905           return 0;\r
6906         }\r
6907         SendMessage(hwnd, WM_COPY, 0, 0);\r
6908         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6909         SendMessage(hInput, WM_PASTE, 0, 0);\r
6910         SetFocus(hInput);\r
6911         return 0;\r
6912       }\r
6913     case IDM_Cut:\r
6914       SendMessage(hwnd, WM_CUT, 0, 0);\r
6915       return 0;\r
6916     case IDM_Paste:\r
6917       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6918       return 0;\r
6919     case IDM_Copy:\r
6920       SendMessage(hwnd, WM_COPY, 0, 0);\r
6921       return 0;\r
6922     default:\r
6923       {\r
6924         int i = LOWORD(wParam) - IDM_CommandX;\r
6925         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6926             icsTextMenuEntry[i].command != NULL) {\r
6927           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6928                    icsTextMenuEntry[i].getname,\r
6929                    icsTextMenuEntry[i].immediate);\r
6930           return 0;\r
6931         }\r
6932       }\r
6933       break;\r
6934     }\r
6935     break;\r
6936   }\r
6937   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6938 }\r
6939 \r
6940 WNDPROC consoleInputWindowProc;\r
6941 \r
6942 LRESULT CALLBACK\r
6943 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6944 {\r
6945   char buf[MSG_SIZ];\r
6946   char *p;\r
6947   static BOOL sendNextChar = FALSE;\r
6948   static BOOL quoteNextChar = FALSE;\r
6949   InputSource *is = consoleInputSource;\r
6950   CHARFORMAT cf;\r
6951   CHARRANGE sel;\r
6952 \r
6953   switch (message) {\r
6954   case WM_CHAR:\r
6955     if (!appData.localLineEditing || sendNextChar) {\r
6956       is->buf[0] = (CHAR) wParam;\r
6957       is->count = 1;\r
6958       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6959       sendNextChar = FALSE;\r
6960       return 0;\r
6961     }\r
6962     if (quoteNextChar) {\r
6963       buf[0] = (char) wParam;\r
6964       buf[1] = NULLCHAR;\r
6965       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6966       quoteNextChar = FALSE;\r
6967       return 0;\r
6968     }\r
6969     switch (wParam) {\r
6970     case '\r':   /* Enter key */\r
6971       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6972       if (consoleEcho) SaveInHistory(is->buf);\r
6973       is->buf[is->count++] = '\n';\r
6974       is->buf[is->count] = NULLCHAR;\r
6975       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6976       if (consoleEcho) {\r
6977         ConsoleOutput(is->buf, is->count, TRUE);\r
6978       } else if (appData.localLineEditing) {\r
6979         ConsoleOutput("\n", 1, TRUE);\r
6980       }\r
6981       /* fall thru */\r
6982     case '\033': /* Escape key */\r
6983       SetWindowText(hwnd, "");\r
6984       cf.cbSize = sizeof(CHARFORMAT);\r
6985       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6986       if (consoleEcho) {\r
6987         cf.crTextColor = textAttribs[ColorNormal].color;\r
6988       } else {\r
6989         cf.crTextColor = COLOR_ECHOOFF;\r
6990       }\r
6991       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6992       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6993       return 0;\r
6994     case '\t':   /* Tab key */\r
6995       if (GetKeyState(VK_SHIFT) < 0) {\r
6996         /* shifted */\r
6997         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6998       } else {\r
6999         /* unshifted */\r
7000         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7001         if (buttonDesc[0].hwnd) {\r
7002           SetFocus(buttonDesc[0].hwnd);\r
7003         } else {\r
7004           SetFocus(hwndMain);\r
7005         }\r
7006       }\r
7007       return 0;\r
7008     case '\023': /* Ctrl+S */\r
7009       sendNextChar = TRUE;\r
7010       return 0;\r
7011     case '\021': /* Ctrl+Q */\r
7012       quoteNextChar = TRUE;\r
7013       return 0;\r
7014     JAWS_REPLAY\r
7015     default:\r
7016       break;\r
7017     }\r
7018     break;\r
7019   case WM_KEYDOWN:\r
7020     switch (wParam) {\r
7021     case VK_UP:\r
7022       GetWindowText(hwnd, buf, MSG_SIZ);\r
7023       p = PrevInHistory(buf);\r
7024       if (p != NULL) {\r
7025         SetWindowText(hwnd, p);\r
7026         sel.cpMin = 999999;\r
7027         sel.cpMax = 999999;\r
7028         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7029         return 0;\r
7030       }\r
7031       break;\r
7032     case VK_DOWN:\r
7033       p = NextInHistory();\r
7034       if (p != NULL) {\r
7035         SetWindowText(hwnd, p);\r
7036         sel.cpMin = 999999;\r
7037         sel.cpMax = 999999;\r
7038         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7039         return 0;\r
7040       }\r
7041       break;\r
7042     case VK_HOME:\r
7043     case VK_END:\r
7044       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7045       /* fall thru */\r
7046     case VK_PRIOR:\r
7047     case VK_NEXT:\r
7048       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7049       return 0;\r
7050     }\r
7051     break;\r
7052   case WM_MBUTTONDOWN:\r
7053     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7054       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7055     break;\r
7056   case WM_RBUTTONUP:\r
7057     if (GetKeyState(VK_SHIFT) & ~1) {\r
7058       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7059         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7060     } else {\r
7061       POINT pt;\r
7062       HMENU hmenu;\r
7063       hmenu = LoadMenu(hInst, "InputMenu");\r
7064       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7065       if (sel.cpMin == sel.cpMax) {\r
7066         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7067         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7068       }\r
7069       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7070         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7071       }\r
7072       pt.x = LOWORD(lParam);\r
7073       pt.y = HIWORD(lParam);\r
7074       MenuPopup(hwnd, pt, hmenu, -1);\r
7075     }\r
7076     return 0;\r
7077   case WM_COMMAND:\r
7078     switch (LOWORD(wParam)) { \r
7079     case IDM_Undo:\r
7080       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7081       return 0;\r
7082     case IDM_SelectAll:\r
7083       sel.cpMin = 0;\r
7084       sel.cpMax = -1; /*999999?*/\r
7085       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7086       return 0;\r
7087     case IDM_Cut:\r
7088       SendMessage(hwnd, WM_CUT, 0, 0);\r
7089       return 0;\r
7090     case IDM_Paste:\r
7091       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7092       return 0;\r
7093     case IDM_Copy:\r
7094       SendMessage(hwnd, WM_COPY, 0, 0);\r
7095       return 0;\r
7096     }\r
7097     break;\r
7098   }\r
7099   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7100 }\r
7101 \r
7102 #define CO_MAX  100000\r
7103 #define CO_TRIM   1000\r
7104 \r
7105 LRESULT CALLBACK\r
7106 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7107 {\r
7108   static SnapData sd;\r
7109   HWND hText, hInput;\r
7110   RECT rect;\r
7111   static int sizeX, sizeY;\r
7112   int newSizeX, newSizeY;\r
7113   MINMAXINFO *mmi;\r
7114   WORD wMask;\r
7115 \r
7116   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7117   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7118 \r
7119   switch (message) {\r
7120   case WM_NOTIFY:\r
7121     if (((NMHDR*)lParam)->code == EN_LINK)\r
7122     {\r
7123       ENLINK *pLink = (ENLINK*)lParam;\r
7124       if (pLink->msg == WM_LBUTTONUP)\r
7125       {\r
7126         TEXTRANGE tr;\r
7127 \r
7128         tr.chrg = pLink->chrg;\r
7129         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7130         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7131         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7132         free(tr.lpstrText);\r
7133       }\r
7134     }\r
7135     break;\r
7136   case WM_INITDIALOG: /* message: initialize dialog box */\r
7137     hwndConsole = hDlg;\r
7138     SetFocus(hInput);\r
7139     consoleTextWindowProc = (WNDPROC)\r
7140       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7141     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7142     consoleInputWindowProc = (WNDPROC)\r
7143       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7144     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7145     Colorize(ColorNormal, TRUE);\r
7146     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7147     ChangedConsoleFont();\r
7148     GetClientRect(hDlg, &rect);\r
7149     sizeX = rect.right;\r
7150     sizeY = rect.bottom;\r
7151     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7152         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7153       WINDOWPLACEMENT wp;\r
7154       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7155       wp.length = sizeof(WINDOWPLACEMENT);\r
7156       wp.flags = 0;\r
7157       wp.showCmd = SW_SHOW;\r
7158       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7159       wp.rcNormalPosition.left = wpConsole.x;\r
7160       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7161       wp.rcNormalPosition.top = wpConsole.y;\r
7162       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7163       SetWindowPlacement(hDlg, &wp);\r
7164     }\r
7165 \r
7166    // [HGM] Chessknight's change 2004-07-13\r
7167    else { /* Determine Defaults */\r
7168        WINDOWPLACEMENT wp;\r
7169        wpConsole.x = wpMain.width + 1;\r
7170        wpConsole.y = wpMain.y;\r
7171        wpConsole.width = screenWidth -  wpMain.width;\r
7172        wpConsole.height = wpMain.height;\r
7173        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7174        wp.length = sizeof(WINDOWPLACEMENT);\r
7175        wp.flags = 0;\r
7176        wp.showCmd = SW_SHOW;\r
7177        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7178        wp.rcNormalPosition.left = wpConsole.x;\r
7179        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7180        wp.rcNormalPosition.top = wpConsole.y;\r
7181        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7182        SetWindowPlacement(hDlg, &wp);\r
7183     }\r
7184 \r
7185    // Allow hText to highlight URLs and send notifications on them\r
7186    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7187    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7188    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7189    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7190 \r
7191     return FALSE;\r
7192 \r
7193   case WM_SETFOCUS:\r
7194     SetFocus(hInput);\r
7195     return 0;\r
7196 \r
7197   case WM_CLOSE:\r
7198     ExitEvent(0);\r
7199     /* not reached */\r
7200     break;\r
7201 \r
7202   case WM_SIZE:\r
7203     if (IsIconic(hDlg)) break;\r
7204     newSizeX = LOWORD(lParam);\r
7205     newSizeY = HIWORD(lParam);\r
7206     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7207       RECT rectText, rectInput;\r
7208       POINT pt;\r
7209       int newTextHeight, newTextWidth;\r
7210       GetWindowRect(hText, &rectText);\r
7211       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7212       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7213       if (newTextHeight < 0) {\r
7214         newSizeY += -newTextHeight;\r
7215         newTextHeight = 0;\r
7216       }\r
7217       SetWindowPos(hText, NULL, 0, 0,\r
7218         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7219       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7220       pt.x = rectInput.left;\r
7221       pt.y = rectInput.top + newSizeY - sizeY;\r
7222       ScreenToClient(hDlg, &pt);\r
7223       SetWindowPos(hInput, NULL, \r
7224         pt.x, pt.y, /* needs client coords */   \r
7225         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7226         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7227     }\r
7228     sizeX = newSizeX;\r
7229     sizeY = newSizeY;\r
7230     break;\r
7231 \r
7232   case WM_GETMINMAXINFO:\r
7233     /* Prevent resizing window too small */\r
7234     mmi = (MINMAXINFO *) lParam;\r
7235     mmi->ptMinTrackSize.x = 100;\r
7236     mmi->ptMinTrackSize.y = 100;\r
7237     break;\r
7238 \r
7239   /* [AS] Snapping */\r
7240   case WM_ENTERSIZEMOVE:\r
7241     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7242 \r
7243   case WM_SIZING:\r
7244     return OnSizing( &sd, hDlg, wParam, lParam );\r
7245 \r
7246   case WM_MOVING:\r
7247     return OnMoving( &sd, hDlg, wParam, lParam );\r
7248 \r
7249   case WM_EXITSIZEMOVE:\r
7250         UpdateICSWidth(hText);\r
7251     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7252   }\r
7253 \r
7254   return DefWindowProc(hDlg, message, wParam, lParam);\r
7255 }\r
7256 \r
7257 \r
7258 VOID\r
7259 ConsoleCreate()\r
7260 {\r
7261   HWND hCons;\r
7262   if (hwndConsole) return;\r
7263   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7264   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7265 }\r
7266 \r
7267 \r
7268 VOID\r
7269 ConsoleOutput(char* data, int length, int forceVisible)\r
7270 {\r
7271   HWND hText;\r
7272   int trim, exlen;\r
7273   char *p, *q;\r
7274   char buf[CO_MAX+1];\r
7275   POINT pEnd;\r
7276   RECT rect;\r
7277   static int delayLF = 0;\r
7278   CHARRANGE savesel, sel;\r
7279 \r
7280   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7281   p = data;\r
7282   q = buf;\r
7283   if (delayLF) {\r
7284     *q++ = '\r';\r
7285     *q++ = '\n';\r
7286     delayLF = 0;\r
7287   }\r
7288   while (length--) {\r
7289     if (*p == '\n') {\r
7290       if (*++p) {\r
7291         *q++ = '\r';\r
7292         *q++ = '\n';\r
7293       } else {\r
7294         delayLF = 1;\r
7295       }\r
7296     } else if (*p == '\007') {\r
7297        MyPlaySound(&sounds[(int)SoundBell]);\r
7298        p++;\r
7299     } else {\r
7300       *q++ = *p++;\r
7301     }\r
7302   }\r
7303   *q = NULLCHAR;\r
7304   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7305   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7306   /* Save current selection */\r
7307   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7308   exlen = GetWindowTextLength(hText);\r
7309   /* Find out whether current end of text is visible */\r
7310   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7311   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7312   /* Trim existing text if it's too long */\r
7313   if (exlen + (q - buf) > CO_MAX) {\r
7314     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7315     sel.cpMin = 0;\r
7316     sel.cpMax = trim;\r
7317     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7318     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7319     exlen -= trim;\r
7320     savesel.cpMin -= trim;\r
7321     savesel.cpMax -= trim;\r
7322     if (exlen < 0) exlen = 0;\r
7323     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7324     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7325   }\r
7326   /* Append the new text */\r
7327   sel.cpMin = exlen;\r
7328   sel.cpMax = exlen;\r
7329   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7330   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7331   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7332   if (forceVisible || exlen == 0 ||\r
7333       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7334        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7335     /* Scroll to make new end of text visible if old end of text\r
7336        was visible or new text is an echo of user typein */\r
7337     sel.cpMin = 9999999;\r
7338     sel.cpMax = 9999999;\r
7339     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7340     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7341     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7342     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7343   }\r
7344   if (savesel.cpMax == exlen || forceVisible) {\r
7345     /* Move insert point to new end of text if it was at the old\r
7346        end of text or if the new text is an echo of user typein */\r
7347     sel.cpMin = 9999999;\r
7348     sel.cpMax = 9999999;\r
7349     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7350   } else {\r
7351     /* Restore previous selection */\r
7352     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7353   }\r
7354   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7355 }\r
7356 \r
7357 /*---------*/\r
7358 \r
7359 \r
7360 void\r
7361 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7362 {\r
7363   char buf[100];\r
7364   char *str;\r
7365   COLORREF oldFg, oldBg;\r
7366   HFONT oldFont;\r
7367   RECT rect;\r
7368 \r
7369   if(copyNumber > 1)
7370     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7371 \r
7372   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7373   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7374   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7375 \r
7376   rect.left = x;\r
7377   rect.right = x + squareSize;\r
7378   rect.top  = y;\r
7379   rect.bottom = y + squareSize;\r
7380   str = buf;\r
7381 \r
7382   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7383                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7384              y, ETO_CLIPPED|ETO_OPAQUE,\r
7385              &rect, str, strlen(str), NULL);\r
7386 \r
7387   (void) SetTextColor(hdc, oldFg);\r
7388   (void) SetBkColor(hdc, oldBg);\r
7389   (void) SelectObject(hdc, oldFont);\r
7390 }\r
7391 \r
7392 void\r
7393 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7394               RECT *rect, char *color, char *flagFell)\r
7395 {\r
7396   char buf[100];\r
7397   char *str;\r
7398   COLORREF oldFg, oldBg;\r
7399   HFONT oldFont;\r
7400 \r
7401   if (appData.clockMode) {\r
7402     if (tinyLayout)\r
7403       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7404     else\r
7405       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7406     str = buf;\r
7407   } else {\r
7408     str = color;\r
7409   }\r
7410 \r
7411   if (highlight) {\r
7412     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7413     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7414   } else {\r
7415     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7416     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7417   }\r
7418   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7419 \r
7420   JAWS_SILENCE\r
7421 \r
7422   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7423              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7424              rect, str, strlen(str), NULL);\r
7425   if(logoHeight > 0 && appData.clockMode) {\r
7426       RECT r;\r
7427       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
7428       r.top = rect->top + logoHeight/2;\r
7429       r.left = rect->left;\r
7430       r.right = rect->right;\r
7431       r.bottom = rect->bottom;\r
7432       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7433                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7434                  &r, str, strlen(str), NULL);\r
7435   }\r
7436   (void) SetTextColor(hdc, oldFg);\r
7437   (void) SetBkColor(hdc, oldBg);\r
7438   (void) SelectObject(hdc, oldFont);\r
7439 }\r
7440 \r
7441 \r
7442 int\r
7443 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7444            OVERLAPPED *ovl)\r
7445 {\r
7446   int ok, err;\r
7447 \r
7448   /* [AS]  */\r
7449   if( count <= 0 ) {\r
7450     if (appData.debugMode) {\r
7451       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7452     }\r
7453 \r
7454     return ERROR_INVALID_USER_BUFFER;\r
7455   }\r
7456 \r
7457   ResetEvent(ovl->hEvent);\r
7458   ovl->Offset = ovl->OffsetHigh = 0;\r
7459   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7460   if (ok) {\r
7461     err = NO_ERROR;\r
7462   } else {\r
7463     err = GetLastError();\r
7464     if (err == ERROR_IO_PENDING) {\r
7465       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7466       if (ok)\r
7467         err = NO_ERROR;\r
7468       else\r
7469         err = GetLastError();\r
7470     }\r
7471   }\r
7472   return err;\r
7473 }\r
7474 \r
7475 int\r
7476 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7477             OVERLAPPED *ovl)\r
7478 {\r
7479   int ok, err;\r
7480 \r
7481   ResetEvent(ovl->hEvent);\r
7482   ovl->Offset = ovl->OffsetHigh = 0;\r
7483   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7484   if (ok) {\r
7485     err = NO_ERROR;\r
7486   } else {\r
7487     err = GetLastError();\r
7488     if (err == ERROR_IO_PENDING) {\r
7489       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7490       if (ok)\r
7491         err = NO_ERROR;\r
7492       else\r
7493         err = GetLastError();\r
7494     }\r
7495   }\r
7496   return err;\r
7497 }\r
7498 \r
7499 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7500 void CheckForInputBufferFull( InputSource * is )\r
7501 {\r
7502     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7503         /* Look for end of line */\r
7504         char * p = is->buf;\r
7505         \r
7506         while( p < is->next && *p != '\n' ) {\r
7507             p++;\r
7508         }\r
7509 \r
7510         if( p >= is->next ) {\r
7511             if (appData.debugMode) {\r
7512                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7513             }\r
7514 \r
7515             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7516             is->count = (DWORD) -1;\r
7517             is->next = is->buf;\r
7518         }\r
7519     }\r
7520 }\r
7521 \r
7522 DWORD\r
7523 InputThread(LPVOID arg)\r
7524 {\r
7525   InputSource *is;\r
7526   OVERLAPPED ovl;\r
7527 \r
7528   is = (InputSource *) arg;\r
7529   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7530   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7531   while (is->hThread != NULL) {\r
7532     is->error = DoReadFile(is->hFile, is->next,\r
7533                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7534                            &is->count, &ovl);\r
7535     if (is->error == NO_ERROR) {\r
7536       is->next += is->count;\r
7537     } else {\r
7538       if (is->error == ERROR_BROKEN_PIPE) {\r
7539         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7540         is->count = 0;\r
7541       } else {\r
7542         is->count = (DWORD) -1;\r
7543         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7544         break; \r
7545       }\r
7546     }\r
7547 \r
7548     CheckForInputBufferFull( is );\r
7549 \r
7550     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7551 \r
7552     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7553 \r
7554     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7555   }\r
7556 \r
7557   CloseHandle(ovl.hEvent);\r
7558   CloseHandle(is->hFile);\r
7559 \r
7560   if (appData.debugMode) {\r
7561     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7562   }\r
7563 \r
7564   return 0;\r
7565 }\r
7566 \r
7567 \r
7568 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7569 DWORD\r
7570 NonOvlInputThread(LPVOID arg)\r
7571 {\r
7572   InputSource *is;\r
7573   char *p, *q;\r
7574   int i;\r
7575   char prev;\r
7576 \r
7577   is = (InputSource *) arg;\r
7578   while (is->hThread != NULL) {\r
7579     is->error = ReadFile(is->hFile, is->next,\r
7580                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7581                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7582     if (is->error == NO_ERROR) {\r
7583       /* Change CRLF to LF */\r
7584       if (is->next > is->buf) {\r
7585         p = is->next - 1;\r
7586         i = is->count + 1;\r
7587       } else {\r
7588         p = is->next;\r
7589         i = is->count;\r
7590       }\r
7591       q = p;\r
7592       prev = NULLCHAR;\r
7593       while (i > 0) {\r
7594         if (prev == '\r' && *p == '\n') {\r
7595           *(q-1) = '\n';\r
7596           is->count--;\r
7597         } else { \r
7598           *q++ = *p;\r
7599         }\r
7600         prev = *p++;\r
7601         i--;\r
7602       }\r
7603       *q = NULLCHAR;\r
7604       is->next = q;\r
7605     } else {\r
7606       if (is->error == ERROR_BROKEN_PIPE) {\r
7607         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7608         is->count = 0; \r
7609       } else {\r
7610         is->count = (DWORD) -1;\r
7611       }\r
7612     }\r
7613 \r
7614     CheckForInputBufferFull( is );\r
7615 \r
7616     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7617 \r
7618     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7619 \r
7620     if (is->count < 0) break;  /* Quit on error */\r
7621   }\r
7622   CloseHandle(is->hFile);\r
7623   return 0;\r
7624 }\r
7625 \r
7626 DWORD\r
7627 SocketInputThread(LPVOID arg)\r
7628 {\r
7629   InputSource *is;\r
7630 \r
7631   is = (InputSource *) arg;\r
7632   while (is->hThread != NULL) {\r
7633     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7634     if ((int)is->count == SOCKET_ERROR) {\r
7635       is->count = (DWORD) -1;\r
7636       is->error = WSAGetLastError();\r
7637     } else {\r
7638       is->error = NO_ERROR;\r
7639       is->next += is->count;\r
7640       if (is->count == 0 && is->second == is) {\r
7641         /* End of file on stderr; quit with no message */\r
7642         break;\r
7643       }\r
7644     }\r
7645     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7646 \r
7647     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7648 \r
7649     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7650   }\r
7651   return 0;\r
7652 }\r
7653 \r
7654 VOID\r
7655 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7656 {\r
7657   InputSource *is;\r
7658 \r
7659   is = (InputSource *) lParam;\r
7660   if (is->lineByLine) {\r
7661     /* Feed in lines one by one */\r
7662     char *p = is->buf;\r
7663     char *q = p;\r
7664     while (q < is->next) {\r
7665       if (*q++ == '\n') {\r
7666         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7667         p = q;\r
7668       }\r
7669     }\r
7670     \r
7671     /* Move any partial line to the start of the buffer */\r
7672     q = is->buf;\r
7673     while (p < is->next) {\r
7674       *q++ = *p++;\r
7675     }\r
7676     is->next = q;\r
7677 \r
7678     if (is->error != NO_ERROR || is->count == 0) {\r
7679       /* Notify backend of the error.  Note: If there was a partial\r
7680          line at the end, it is not flushed through. */\r
7681       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7682     }\r
7683   } else {\r
7684     /* Feed in the whole chunk of input at once */\r
7685     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7686     is->next = is->buf;\r
7687   }\r
7688 }\r
7689 \r
7690 /*---------------------------------------------------------------------------*\\r
7691  *\r
7692  *  Menu enables. Used when setting various modes.\r
7693  *\r
7694 \*---------------------------------------------------------------------------*/\r
7695 \r
7696 typedef struct {\r
7697   int item;\r
7698   int flags;\r
7699 } Enables;\r
7700 \r
7701 VOID\r
7702 GreyRevert(Boolean grey)\r
7703 { // [HGM] vari: for retracting variations in local mode\r
7704   HMENU hmenu = GetMenu(hwndMain);\r
7705   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7706   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7707 }\r
7708 \r
7709 VOID\r
7710 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7711 {\r
7712   while (enab->item > 0) {\r
7713     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7714     enab++;\r
7715   }\r
7716 }\r
7717 \r
7718 Enables gnuEnables[] = {\r
7719   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7720   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7721   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7722   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7723   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7726   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7729   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7730   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7731   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7732   { -1, -1 }\r
7733 };\r
7734 \r
7735 Enables icsEnables[] = {\r
7736   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7744   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7750   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7753   { -1, -1 }\r
7754 };\r
7755 \r
7756 #if ZIPPY\r
7757 Enables zippyEnables[] = {\r
7758   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7759   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7760   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7761   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7762   { -1, -1 }\r
7763 };\r
7764 #endif\r
7765 \r
7766 Enables ncpEnables[] = {\r
7767   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7776   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7777   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7788   { -1, -1 }\r
7789 };\r
7790 \r
7791 Enables trainingOnEnables[] = {\r
7792   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7800   { -1, -1 }\r
7801 };\r
7802 \r
7803 Enables trainingOffEnables[] = {\r
7804   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7806   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7812   { -1, -1 }\r
7813 };\r
7814 \r
7815 /* These modify either ncpEnables or gnuEnables */\r
7816 Enables cmailEnables[] = {\r
7817   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7818   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7819   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7820   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7822   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7824   { -1, -1 }\r
7825 };\r
7826 \r
7827 Enables machineThinkingEnables[] = {\r
7828   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7844   { -1, -1 }\r
7845 };\r
7846 \r
7847 Enables userThinkingEnables[] = {\r
7848   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7849   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7850   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7851   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7864   { -1, -1 }\r
7865 };\r
7866 \r
7867 /*---------------------------------------------------------------------------*\\r
7868  *\r
7869  *  Front-end interface functions exported by XBoard.\r
7870  *  Functions appear in same order as prototypes in frontend.h.\r
7871  * \r
7872 \*---------------------------------------------------------------------------*/\r
7873 VOID\r
7874 ModeHighlight()\r
7875 {\r
7876   static UINT prevChecked = 0;\r
7877   static int prevPausing = 0;\r
7878   UINT nowChecked;\r
7879 \r
7880   if (pausing != prevPausing) {\r
7881     prevPausing = pausing;\r
7882     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7883                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7884     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7885   }\r
7886 \r
7887   switch (gameMode) {\r
7888   case BeginningOfGame:\r
7889     if (appData.icsActive)\r
7890       nowChecked = IDM_IcsClient;\r
7891     else if (appData.noChessProgram)\r
7892       nowChecked = IDM_EditGame;\r
7893     else\r
7894       nowChecked = IDM_MachineBlack;\r
7895     break;\r
7896   case MachinePlaysBlack:\r
7897     nowChecked = IDM_MachineBlack;\r
7898     break;\r
7899   case MachinePlaysWhite:\r
7900     nowChecked = IDM_MachineWhite;\r
7901     break;\r
7902   case TwoMachinesPlay:\r
7903     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7904     break;\r
7905   case AnalyzeMode:\r
7906     nowChecked = IDM_AnalysisMode;\r
7907     break;\r
7908   case AnalyzeFile:\r
7909     nowChecked = IDM_AnalyzeFile;\r
7910     break;\r
7911   case EditGame:\r
7912     nowChecked = IDM_EditGame;\r
7913     break;\r
7914   case PlayFromGameFile:\r
7915     nowChecked = IDM_LoadGame;\r
7916     break;\r
7917   case EditPosition:\r
7918     nowChecked = IDM_EditPosition;\r
7919     break;\r
7920   case Training:\r
7921     nowChecked = IDM_Training;\r
7922     break;\r
7923   case IcsPlayingWhite:\r
7924   case IcsPlayingBlack:\r
7925   case IcsObserving:\r
7926   case IcsIdle:\r
7927     nowChecked = IDM_IcsClient;\r
7928     break;\r
7929   default:\r
7930   case EndOfGame:\r
7931     nowChecked = 0;\r
7932     break;\r
7933   }\r
7934   if (prevChecked != 0)\r
7935     (void) CheckMenuItem(GetMenu(hwndMain),\r
7936                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7937   if (nowChecked != 0)\r
7938     (void) CheckMenuItem(GetMenu(hwndMain),\r
7939                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7940 \r
7941   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7942     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7943                           MF_BYCOMMAND|MF_ENABLED);\r
7944   } else {\r
7945     (void) EnableMenuItem(GetMenu(hwndMain), \r
7946                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7947   }\r
7948 \r
7949   prevChecked = nowChecked;\r
7950 \r
7951   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7952   if (appData.icsActive) {\r
7953        if (appData.icsEngineAnalyze) {\r
7954                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7955                        MF_BYCOMMAND|MF_CHECKED);\r
7956        } else {\r
7957                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7958                        MF_BYCOMMAND|MF_UNCHECKED);\r
7959        }\r
7960   }\r
7961 }\r
7962 \r
7963 VOID\r
7964 SetICSMode()\r
7965 {\r
7966   HMENU hmenu = GetMenu(hwndMain);\r
7967   SetMenuEnables(hmenu, icsEnables);\r
7968   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7969     MF_BYPOSITION|MF_ENABLED);\r
7970 #if ZIPPY\r
7971   if (appData.zippyPlay) {\r
7972     SetMenuEnables(hmenu, zippyEnables);\r
7973     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7974          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7975           MF_BYCOMMAND|MF_ENABLED);\r
7976   }\r
7977 #endif\r
7978 }\r
7979 \r
7980 VOID\r
7981 SetGNUMode()\r
7982 {\r
7983   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7984 }\r
7985 \r
7986 VOID\r
7987 SetNCPMode()\r
7988 {\r
7989   HMENU hmenu = GetMenu(hwndMain);\r
7990   SetMenuEnables(hmenu, ncpEnables);\r
7991   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7992     MF_BYPOSITION|MF_GRAYED);\r
7993     DrawMenuBar(hwndMain);\r
7994 }\r
7995 \r
7996 VOID\r
7997 SetCmailMode()\r
7998 {\r
7999   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8000 }\r
8001 \r
8002 VOID \r
8003 SetTrainingModeOn()\r
8004 {\r
8005   int i;\r
8006   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8007   for (i = 0; i < N_BUTTONS; i++) {\r
8008     if (buttonDesc[i].hwnd != NULL)\r
8009       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8010   }\r
8011   CommentPopDown();\r
8012 }\r
8013 \r
8014 VOID SetTrainingModeOff()\r
8015 {\r
8016   int i;\r
8017   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8018   for (i = 0; i < N_BUTTONS; i++) {\r
8019     if (buttonDesc[i].hwnd != NULL)\r
8020       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8021   }\r
8022 }\r
8023 \r
8024 \r
8025 VOID\r
8026 SetUserThinkingEnables()\r
8027 {\r
8028   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8029 }\r
8030 \r
8031 VOID\r
8032 SetMachineThinkingEnables()\r
8033 {\r
8034   HMENU hMenu = GetMenu(hwndMain);\r
8035   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8036 \r
8037   SetMenuEnables(hMenu, machineThinkingEnables);\r
8038 \r
8039   if (gameMode == MachinePlaysBlack) {\r
8040     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8041   } else if (gameMode == MachinePlaysWhite) {\r
8042     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8043   } else if (gameMode == TwoMachinesPlay) {\r
8044     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8045   }\r
8046 }\r
8047 \r
8048 \r
8049 VOID\r
8050 DisplayTitle(char *str)\r
8051 {\r
8052   char title[MSG_SIZ], *host;\r
8053   if (str[0] != NULLCHAR) {\r
8054     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8055   } else if (appData.icsActive) {\r
8056     if (appData.icsCommPort[0] != NULLCHAR)\r
8057       host = "ICS";\r
8058     else \r
8059       host = appData.icsHost;\r
8060       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8061   } else if (appData.noChessProgram) {\r
8062     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8063   } else {\r
8064     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8065     strcat(title, ": ");\r
8066     strcat(title, first.tidy);\r
8067   }\r
8068   SetWindowText(hwndMain, title);\r
8069 }\r
8070 \r
8071 \r
8072 VOID\r
8073 DisplayMessage(char *str1, char *str2)\r
8074 {\r
8075   HDC hdc;\r
8076   HFONT oldFont;\r
8077   int remain = MESSAGE_TEXT_MAX - 1;\r
8078   int len;\r
8079 \r
8080   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8081   messageText[0] = NULLCHAR;\r
8082   if (*str1) {\r
8083     len = strlen(str1);\r
8084     if (len > remain) len = remain;\r
8085     strncpy(messageText, str1, len);\r
8086     messageText[len] = NULLCHAR;\r
8087     remain -= len;\r
8088   }\r
8089   if (*str2 && remain >= 2) {\r
8090     if (*str1) {\r
8091       strcat(messageText, "  ");\r
8092       remain -= 2;\r
8093     }\r
8094     len = strlen(str2);\r
8095     if (len > remain) len = remain;\r
8096     strncat(messageText, str2, len);\r
8097   }\r
8098   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8099 \r
8100   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8101 \r
8102   SAYMACHINEMOVE();\r
8103 \r
8104   hdc = GetDC(hwndMain);\r
8105   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8106   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8107              &messageRect, messageText, strlen(messageText), NULL);\r
8108   (void) SelectObject(hdc, oldFont);\r
8109   (void) ReleaseDC(hwndMain, hdc);\r
8110 }\r
8111 \r
8112 VOID\r
8113 DisplayError(char *str, int error)\r
8114 {\r
8115   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8116   int len;\r
8117 \r
8118   if (error == 0) {\r
8119     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8120   } else {\r
8121     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8122                         NULL, error, LANG_NEUTRAL,\r
8123                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8124     if (len > 0) {\r
8125       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8126     } else {\r
8127       ErrorMap *em = errmap;\r
8128       while (em->err != 0 && em->err != error) em++;\r
8129       if (em->err != 0) {\r
8130         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8131       } else {\r
8132         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8133       }\r
8134     }\r
8135   }\r
8136   \r
8137   ErrorPopUp(_("Error"), buf);\r
8138 }\r
8139 \r
8140 \r
8141 VOID\r
8142 DisplayMoveError(char *str)\r
8143 {\r
8144   fromX = fromY = -1;\r
8145   ClearHighlights();\r
8146   DrawPosition(FALSE, NULL);\r
8147   if (appData.popupMoveErrors) {\r
8148     ErrorPopUp(_("Error"), str);\r
8149   } else {\r
8150     DisplayMessage(str, "");\r
8151     moveErrorMessageUp = TRUE;\r
8152   }\r
8153 }\r
8154 \r
8155 VOID\r
8156 DisplayFatalError(char *str, int error, int exitStatus)\r
8157 {\r
8158   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8159   int len;\r
8160   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8161 \r
8162   if (error != 0) {\r
8163     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8164                         NULL, error, LANG_NEUTRAL,\r
8165                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8166     if (len > 0) {\r
8167       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8168     } else {\r
8169       ErrorMap *em = errmap;\r
8170       while (em->err != 0 && em->err != error) em++;\r
8171       if (em->err != 0) {\r
8172         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8173       } else {\r
8174         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8175       }\r
8176     }\r
8177     str = buf;\r
8178   }\r
8179   if (appData.debugMode) {\r
8180     fprintf(debugFP, "%s: %s\n", label, str);\r
8181   }\r
8182   if (appData.popupExitMessage) {\r
8183     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8184                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8185   }\r
8186   ExitEvent(exitStatus);\r
8187 }\r
8188 \r
8189 \r
8190 VOID\r
8191 DisplayInformation(char *str)\r
8192 {\r
8193   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8194 }\r
8195 \r
8196 \r
8197 VOID\r
8198 DisplayNote(char *str)\r
8199 {\r
8200   ErrorPopUp(_("Note"), str);\r
8201 }\r
8202 \r
8203 \r
8204 typedef struct {\r
8205   char *title, *question, *replyPrefix;\r
8206   ProcRef pr;\r
8207 } QuestionParams;\r
8208 \r
8209 LRESULT CALLBACK\r
8210 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8211 {\r
8212   static QuestionParams *qp;\r
8213   char reply[MSG_SIZ];\r
8214   int len, err;\r
8215 \r
8216   switch (message) {\r
8217   case WM_INITDIALOG:\r
8218     qp = (QuestionParams *) lParam;\r
8219     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8220     Translate(hDlg, DLG_Question);\r
8221     SetWindowText(hDlg, qp->title);\r
8222     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8223     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8224     return FALSE;\r
8225 \r
8226   case WM_COMMAND:\r
8227     switch (LOWORD(wParam)) {\r
8228     case IDOK:\r
8229       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8230       if (*reply) strcat(reply, " ");\r
8231       len = strlen(reply);\r
8232       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8233       strcat(reply, "\n");\r
8234       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8235       EndDialog(hDlg, TRUE);\r
8236       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8237       return TRUE;\r
8238     case IDCANCEL:\r
8239       EndDialog(hDlg, FALSE);\r
8240       return TRUE;\r
8241     default:\r
8242       break;\r
8243     }\r
8244     break;\r
8245   }\r
8246   return FALSE;\r
8247 }\r
8248 \r
8249 VOID\r
8250 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8251 {\r
8252     QuestionParams qp;\r
8253     FARPROC lpProc;\r
8254     \r
8255     qp.title = title;\r
8256     qp.question = question;\r
8257     qp.replyPrefix = replyPrefix;\r
8258     qp.pr = pr;\r
8259     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8260     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8261       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8262     FreeProcInstance(lpProc);\r
8263 }\r
8264 \r
8265 /* [AS] Pick FRC position */\r
8266 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8267 {\r
8268     static int * lpIndexFRC;\r
8269     BOOL index_is_ok;\r
8270     char buf[16];\r
8271 \r
8272     switch( message )\r
8273     {\r
8274     case WM_INITDIALOG:\r
8275         lpIndexFRC = (int *) lParam;\r
8276 \r
8277         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8278         Translate(hDlg, DLG_NewGameFRC);\r
8279 \r
8280         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8281         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8282         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8283         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8284 \r
8285         break;\r
8286 \r
8287     case WM_COMMAND:\r
8288         switch( LOWORD(wParam) ) {\r
8289         case IDOK:\r
8290             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8291             EndDialog( hDlg, 0 );\r
8292             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8293             return TRUE;\r
8294         case IDCANCEL:\r
8295             EndDialog( hDlg, 1 );   \r
8296             return TRUE;\r
8297         case IDC_NFG_Edit:\r
8298             if( HIWORD(wParam) == EN_CHANGE ) {\r
8299                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8300 \r
8301                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8302             }\r
8303             return TRUE;\r
8304         case IDC_NFG_Random:\r
8305           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8306             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8307             return TRUE;\r
8308         }\r
8309 \r
8310         break;\r
8311     }\r
8312 \r
8313     return FALSE;\r
8314 }\r
8315 \r
8316 int NewGameFRC()\r
8317 {\r
8318     int result;\r
8319     int index = appData.defaultFrcPosition;\r
8320     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8321 \r
8322     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8323 \r
8324     if( result == 0 ) {\r
8325         appData.defaultFrcPosition = index;\r
8326     }\r
8327 \r
8328     return result;\r
8329 }\r
8330 \r
8331 /* [AS] Game list options. Refactored by HGM */\r
8332 \r
8333 HWND gameListOptionsDialog;\r
8334 \r
8335 // low-level front-end: clear text edit / list widget\r
8336 void\r
8337 GLT_ClearList()\r
8338 {\r
8339     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8340 }\r
8341 \r
8342 // low-level front-end: clear text edit / list widget\r
8343 void\r
8344 GLT_DeSelectList()\r
8345 {\r
8346     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8347 }\r
8348 \r
8349 // low-level front-end: append line to text edit / list widget\r
8350 void\r
8351 GLT_AddToList( char *name )\r
8352 {\r
8353     if( name != 0 ) {\r
8354             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8355     }\r
8356 }\r
8357 \r
8358 // low-level front-end: get line from text edit / list widget\r
8359 Boolean\r
8360 GLT_GetFromList( int index, char *name )\r
8361 {\r
8362     if( name != 0 ) {\r
8363             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8364                 return TRUE;\r
8365     }\r
8366     return FALSE;\r
8367 }\r
8368 \r
8369 void GLT_MoveSelection( HWND hDlg, int delta )\r
8370 {\r
8371     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8372     int idx2 = idx1 + delta;\r
8373     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8374 \r
8375     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8376         char buf[128];\r
8377 \r
8378         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8379         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8380         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8381         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8382     }\r
8383 }\r
8384 \r
8385 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8386 {\r
8387     switch( message )\r
8388     {\r
8389     case WM_INITDIALOG:\r
8390         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8391         \r
8392         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8393         Translate(hDlg, DLG_GameListOptions);\r
8394 \r
8395         /* Initialize list */\r
8396         GLT_TagsToList( lpUserGLT );\r
8397 \r
8398         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8399 \r
8400         break;\r
8401 \r
8402     case WM_COMMAND:\r
8403         switch( LOWORD(wParam) ) {\r
8404         case IDOK:\r
8405             GLT_ParseList();\r
8406             EndDialog( hDlg, 0 );\r
8407             return TRUE;\r
8408         case IDCANCEL:\r
8409             EndDialog( hDlg, 1 );\r
8410             return TRUE;\r
8411 \r
8412         case IDC_GLT_Default:\r
8413             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8414             return TRUE;\r
8415 \r
8416         case IDC_GLT_Restore:\r
8417             GLT_TagsToList( appData.gameListTags );\r
8418             return TRUE;\r
8419 \r
8420         case IDC_GLT_Up:\r
8421             GLT_MoveSelection( hDlg, -1 );\r
8422             return TRUE;\r
8423 \r
8424         case IDC_GLT_Down:\r
8425             GLT_MoveSelection( hDlg, +1 );\r
8426             return TRUE;\r
8427         }\r
8428 \r
8429         break;\r
8430     }\r
8431 \r
8432     return FALSE;\r
8433 }\r
8434 \r
8435 int GameListOptions()\r
8436 {\r
8437     int result;\r
8438     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8439 \r
8440       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8441 \r
8442     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8443 \r
8444     if( result == 0 ) {\r
8445         /* [AS] Memory leak here! */\r
8446         appData.gameListTags = strdup( lpUserGLT ); \r
8447     }\r
8448 \r
8449     return result;\r
8450 }\r
8451 \r
8452 VOID\r
8453 DisplayIcsInteractionTitle(char *str)\r
8454 {\r
8455   char consoleTitle[MSG_SIZ];\r
8456 \r
8457     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8458   SetWindowText(hwndConsole, consoleTitle);\r
8459 }\r
8460 \r
8461 void\r
8462 DrawPosition(int fullRedraw, Board board)\r
8463 {\r
8464   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8465 }\r
8466 \r
8467 void NotifyFrontendLogin()\r
8468 {\r
8469         if (hwndConsole)\r
8470                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8471 }\r
8472 \r
8473 VOID\r
8474 ResetFrontEnd()\r
8475 {\r
8476   fromX = fromY = -1;\r
8477   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8478     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8479     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8480     dragInfo.lastpos = dragInfo.pos;\r
8481     dragInfo.start.x = dragInfo.start.y = -1;\r
8482     dragInfo.from = dragInfo.start;\r
8483     ReleaseCapture();\r
8484     DrawPosition(TRUE, NULL);\r
8485   }\r
8486   TagsPopDown();\r
8487 }\r
8488 \r
8489 \r
8490 VOID\r
8491 CommentPopUp(char *title, char *str)\r
8492 {\r
8493   HWND hwnd = GetActiveWindow();\r
8494   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8495   SAY(str);\r
8496   SetActiveWindow(hwnd);\r
8497 }\r
8498 \r
8499 VOID\r
8500 CommentPopDown(void)\r
8501 {\r
8502   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8503   if (commentDialog) {\r
8504     ShowWindow(commentDialog, SW_HIDE);\r
8505   }\r
8506   commentUp = FALSE;\r
8507 }\r
8508 \r
8509 VOID\r
8510 EditCommentPopUp(int index, char *title, char *str)\r
8511 {\r
8512   EitherCommentPopUp(index, title, str, TRUE);\r
8513 }\r
8514 \r
8515 \r
8516 VOID\r
8517 RingBell()\r
8518 {\r
8519   MyPlaySound(&sounds[(int)SoundMove]);\r
8520 }\r
8521 \r
8522 VOID PlayIcsWinSound()\r
8523 {\r
8524   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8525 }\r
8526 \r
8527 VOID PlayIcsLossSound()\r
8528 {\r
8529   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8530 }\r
8531 \r
8532 VOID PlayIcsDrawSound()\r
8533 {\r
8534   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8535 }\r
8536 \r
8537 VOID PlayIcsUnfinishedSound()\r
8538 {\r
8539   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8540 }\r
8541 \r
8542 VOID\r
8543 PlayAlarmSound()\r
8544 {\r
8545   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8546 }\r
8547 \r
8548 \r
8549 VOID\r
8550 EchoOn()\r
8551 {\r
8552   HWND hInput;\r
8553   consoleEcho = TRUE;\r
8554   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8555   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8556   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8557 }\r
8558 \r
8559 \r
8560 VOID\r
8561 EchoOff()\r
8562 {\r
8563   CHARFORMAT cf;\r
8564   HWND hInput;\r
8565   consoleEcho = FALSE;\r
8566   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8567   /* This works OK: set text and background both to the same color */\r
8568   cf = consoleCF;\r
8569   cf.crTextColor = COLOR_ECHOOFF;\r
8570   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8571   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8572 }\r
8573 \r
8574 /* No Raw()...? */\r
8575 \r
8576 void Colorize(ColorClass cc, int continuation)\r
8577 {\r
8578   currentColorClass = cc;\r
8579   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8580   consoleCF.crTextColor = textAttribs[cc].color;\r
8581   consoleCF.dwEffects = textAttribs[cc].effects;\r
8582   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8583 }\r
8584 \r
8585 char *\r
8586 UserName()\r
8587 {\r
8588   static char buf[MSG_SIZ];\r
8589   DWORD bufsiz = MSG_SIZ;\r
8590 \r
8591   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8592         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8593   }\r
8594   if (!GetUserName(buf, &bufsiz)) {\r
8595     /*DisplayError("Error getting user name", GetLastError());*/\r
8596     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8597   }\r
8598   return buf;\r
8599 }\r
8600 \r
8601 char *\r
8602 HostName()\r
8603 {\r
8604   static char buf[MSG_SIZ];\r
8605   DWORD bufsiz = MSG_SIZ;\r
8606 \r
8607   if (!GetComputerName(buf, &bufsiz)) {\r
8608     /*DisplayError("Error getting host name", GetLastError());*/\r
8609     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8610   }\r
8611   return buf;\r
8612 }\r
8613 \r
8614 \r
8615 int\r
8616 ClockTimerRunning()\r
8617 {\r
8618   return clockTimerEvent != 0;\r
8619 }\r
8620 \r
8621 int\r
8622 StopClockTimer()\r
8623 {\r
8624   if (clockTimerEvent == 0) return FALSE;\r
8625   KillTimer(hwndMain, clockTimerEvent);\r
8626   clockTimerEvent = 0;\r
8627   return TRUE;\r
8628 }\r
8629 \r
8630 void\r
8631 StartClockTimer(long millisec)\r
8632 {\r
8633   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8634                              (UINT) millisec, NULL);\r
8635 }\r
8636 \r
8637 void\r
8638 DisplayWhiteClock(long timeRemaining, int highlight)\r
8639 {\r
8640   HDC hdc;\r
8641   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8642 \r
8643   if(appData.noGUI) return;\r
8644   hdc = GetDC(hwndMain);\r
8645   if (!IsIconic(hwndMain)) {\r
8646     DisplayAClock(hdc, timeRemaining, highlight, \r
8647                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8648   }\r
8649   if (highlight && iconCurrent == iconBlack) {\r
8650     iconCurrent = iconWhite;\r
8651     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8652     if (IsIconic(hwndMain)) {\r
8653       DrawIcon(hdc, 2, 2, iconCurrent);\r
8654     }\r
8655   }\r
8656   (void) ReleaseDC(hwndMain, hdc);\r
8657   if (hwndConsole)\r
8658     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8659 }\r
8660 \r
8661 void\r
8662 DisplayBlackClock(long timeRemaining, int highlight)\r
8663 {\r
8664   HDC hdc;\r
8665   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8666 \r
8667   if(appData.noGUI) return;\r
8668   hdc = GetDC(hwndMain);\r
8669   if (!IsIconic(hwndMain)) {\r
8670     DisplayAClock(hdc, timeRemaining, highlight, \r
8671                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8672   }\r
8673   if (highlight && iconCurrent == iconWhite) {\r
8674     iconCurrent = iconBlack;\r
8675     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8676     if (IsIconic(hwndMain)) {\r
8677       DrawIcon(hdc, 2, 2, iconCurrent);\r
8678     }\r
8679   }\r
8680   (void) ReleaseDC(hwndMain, hdc);\r
8681   if (hwndConsole)\r
8682     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8683 }\r
8684 \r
8685 \r
8686 int\r
8687 LoadGameTimerRunning()\r
8688 {\r
8689   return loadGameTimerEvent != 0;\r
8690 }\r
8691 \r
8692 int\r
8693 StopLoadGameTimer()\r
8694 {\r
8695   if (loadGameTimerEvent == 0) return FALSE;\r
8696   KillTimer(hwndMain, loadGameTimerEvent);\r
8697   loadGameTimerEvent = 0;\r
8698   return TRUE;\r
8699 }\r
8700 \r
8701 void\r
8702 StartLoadGameTimer(long millisec)\r
8703 {\r
8704   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8705                                 (UINT) millisec, NULL);\r
8706 }\r
8707 \r
8708 void\r
8709 AutoSaveGame()\r
8710 {\r
8711   char *defName;\r
8712   FILE *f;\r
8713   char fileTitle[MSG_SIZ];\r
8714 \r
8715   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8716   f = OpenFileDialog(hwndMain, "a", defName,\r
8717                      appData.oldSaveStyle ? "gam" : "pgn",\r
8718                      GAME_FILT, \r
8719                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8720   if (f != NULL) {\r
8721     SaveGame(f, 0, "");\r
8722     fclose(f);\r
8723   }\r
8724 }\r
8725 \r
8726 \r
8727 void\r
8728 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8729 {\r
8730   if (delayedTimerEvent != 0) {\r
8731     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8732       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8733     }\r
8734     KillTimer(hwndMain, delayedTimerEvent);\r
8735     delayedTimerEvent = 0;\r
8736     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8737     delayedTimerCallback();\r
8738   }\r
8739   delayedTimerCallback = cb;\r
8740   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8741                                 (UINT) millisec, NULL);\r
8742 }\r
8743 \r
8744 DelayedEventCallback\r
8745 GetDelayedEvent()\r
8746 {\r
8747   if (delayedTimerEvent) {\r
8748     return delayedTimerCallback;\r
8749   } else {\r
8750     return NULL;\r
8751   }\r
8752 }\r
8753 \r
8754 void\r
8755 CancelDelayedEvent()\r
8756 {\r
8757   if (delayedTimerEvent) {\r
8758     KillTimer(hwndMain, delayedTimerEvent);\r
8759     delayedTimerEvent = 0;\r
8760   }\r
8761 }\r
8762 \r
8763 DWORD GetWin32Priority(int nice)\r
8764 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8765 /*\r
8766 REALTIME_PRIORITY_CLASS     0x00000100\r
8767 HIGH_PRIORITY_CLASS         0x00000080\r
8768 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8769 NORMAL_PRIORITY_CLASS       0x00000020\r
8770 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8771 IDLE_PRIORITY_CLASS         0x00000040\r
8772 */\r
8773         if (nice < -15) return 0x00000080;\r
8774         if (nice < 0)   return 0x00008000;\r
8775         if (nice == 0)  return 0x00000020;\r
8776         if (nice < 15)  return 0x00004000;\r
8777         return 0x00000040;\r
8778 }\r
8779 \r
8780 /* Start a child process running the given program.\r
8781    The process's standard output can be read from "from", and its\r
8782    standard input can be written to "to".\r
8783    Exit with fatal error if anything goes wrong.\r
8784    Returns an opaque pointer that can be used to destroy the process\r
8785    later.\r
8786 */\r
8787 int\r
8788 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8789 {\r
8790 #define BUFSIZE 4096\r
8791 \r
8792   HANDLE hChildStdinRd, hChildStdinWr,\r
8793     hChildStdoutRd, hChildStdoutWr;\r
8794   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8795   SECURITY_ATTRIBUTES saAttr;\r
8796   BOOL fSuccess;\r
8797   PROCESS_INFORMATION piProcInfo;\r
8798   STARTUPINFO siStartInfo;\r
8799   ChildProc *cp;\r
8800   char buf[MSG_SIZ];\r
8801   DWORD err;\r
8802 \r
8803   if (appData.debugMode) {\r
8804     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8805   }\r
8806 \r
8807   *pr = NoProc;\r
8808 \r
8809   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8810   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8811   saAttr.bInheritHandle = TRUE;\r
8812   saAttr.lpSecurityDescriptor = NULL;\r
8813 \r
8814   /*\r
8815    * The steps for redirecting child's STDOUT:\r
8816    *     1. Create anonymous pipe to be STDOUT for child.\r
8817    *     2. Create a noninheritable duplicate of read handle,\r
8818    *         and close the inheritable read handle.\r
8819    */\r
8820 \r
8821   /* Create a pipe for the child's STDOUT. */\r
8822   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8823     return GetLastError();\r
8824   }\r
8825 \r
8826   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8827   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8828                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8829                              FALSE,     /* not inherited */\r
8830                              DUPLICATE_SAME_ACCESS);\r
8831   if (! fSuccess) {\r
8832     return GetLastError();\r
8833   }\r
8834   CloseHandle(hChildStdoutRd);\r
8835 \r
8836   /*\r
8837    * The steps for redirecting child's STDIN:\r
8838    *     1. Create anonymous pipe to be STDIN for child.\r
8839    *     2. Create a noninheritable duplicate of write handle,\r
8840    *         and close the inheritable write handle.\r
8841    */\r
8842 \r
8843   /* Create a pipe for the child's STDIN. */\r
8844   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8845     return GetLastError();\r
8846   }\r
8847 \r
8848   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8849   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8850                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8851                              FALSE,     /* not inherited */\r
8852                              DUPLICATE_SAME_ACCESS);\r
8853   if (! fSuccess) {\r
8854     return GetLastError();\r
8855   }\r
8856   CloseHandle(hChildStdinWr);\r
8857 \r
8858   /* Arrange to (1) look in dir for the child .exe file, and\r
8859    * (2) have dir be the child's working directory.  Interpret\r
8860    * dir relative to the directory WinBoard loaded from. */\r
8861   GetCurrentDirectory(MSG_SIZ, buf);\r
8862   SetCurrentDirectory(installDir);\r
8863   SetCurrentDirectory(dir);\r
8864 \r
8865   /* Now create the child process. */\r
8866 \r
8867   siStartInfo.cb = sizeof(STARTUPINFO);\r
8868   siStartInfo.lpReserved = NULL;\r
8869   siStartInfo.lpDesktop = NULL;\r
8870   siStartInfo.lpTitle = NULL;\r
8871   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8872   siStartInfo.cbReserved2 = 0;\r
8873   siStartInfo.lpReserved2 = NULL;\r
8874   siStartInfo.hStdInput = hChildStdinRd;\r
8875   siStartInfo.hStdOutput = hChildStdoutWr;\r
8876   siStartInfo.hStdError = hChildStdoutWr;\r
8877 \r
8878   fSuccess = CreateProcess(NULL,\r
8879                            cmdLine,        /* command line */\r
8880                            NULL,           /* process security attributes */\r
8881                            NULL,           /* primary thread security attrs */\r
8882                            TRUE,           /* handles are inherited */\r
8883                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8884                            NULL,           /* use parent's environment */\r
8885                            NULL,\r
8886                            &siStartInfo, /* STARTUPINFO pointer */\r
8887                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8888 \r
8889   err = GetLastError();\r
8890   SetCurrentDirectory(buf); /* return to prev directory */\r
8891   if (! fSuccess) {\r
8892     return err;\r
8893   }\r
8894 \r
8895   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8896     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8897     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8898   }\r
8899 \r
8900   /* Close the handles we don't need in the parent */\r
8901   CloseHandle(piProcInfo.hThread);\r
8902   CloseHandle(hChildStdinRd);\r
8903   CloseHandle(hChildStdoutWr);\r
8904 \r
8905   /* Prepare return value */\r
8906   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8907   cp->kind = CPReal;\r
8908   cp->hProcess = piProcInfo.hProcess;\r
8909   cp->pid = piProcInfo.dwProcessId;\r
8910   cp->hFrom = hChildStdoutRdDup;\r
8911   cp->hTo = hChildStdinWrDup;\r
8912 \r
8913   *pr = (void *) cp;\r
8914 \r
8915   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8916      2000 where engines sometimes don't see the initial command(s)\r
8917      from WinBoard and hang.  I don't understand how that can happen,\r
8918      but the Sleep is harmless, so I've put it in.  Others have also\r
8919      reported what may be the same problem, so hopefully this will fix\r
8920      it for them too.  */\r
8921   Sleep(500);\r
8922 \r
8923   return NO_ERROR;\r
8924 }\r
8925 \r
8926 \r
8927 void\r
8928 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8929 {\r
8930   ChildProc *cp; int result;\r
8931 \r
8932   cp = (ChildProc *) pr;\r
8933   if (cp == NULL) return;\r
8934 \r
8935   switch (cp->kind) {\r
8936   case CPReal:\r
8937     /* TerminateProcess is considered harmful, so... */\r
8938     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8939     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8940     /* The following doesn't work because the chess program\r
8941        doesn't "have the same console" as WinBoard.  Maybe\r
8942        we could arrange for this even though neither WinBoard\r
8943        nor the chess program uses a console for stdio? */\r
8944     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8945 \r
8946     /* [AS] Special termination modes for misbehaving programs... */\r
8947     if( signal == 9 ) { \r
8948         result = TerminateProcess( cp->hProcess, 0 );\r
8949 \r
8950         if ( appData.debugMode) {\r
8951             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8952         }\r
8953     }\r
8954     else if( signal == 10 ) {\r
8955         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8956 \r
8957         if( dw != WAIT_OBJECT_0 ) {\r
8958             result = TerminateProcess( cp->hProcess, 0 );\r
8959 \r
8960             if ( appData.debugMode) {\r
8961                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8962             }\r
8963 \r
8964         }\r
8965     }\r
8966 \r
8967     CloseHandle(cp->hProcess);\r
8968     break;\r
8969 \r
8970   case CPComm:\r
8971     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8972     break;\r
8973 \r
8974   case CPSock:\r
8975     closesocket(cp->sock);\r
8976     WSACleanup();\r
8977     break;\r
8978 \r
8979   case CPRcmd:\r
8980     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8981     closesocket(cp->sock);\r
8982     closesocket(cp->sock2);\r
8983     WSACleanup();\r
8984     break;\r
8985   }\r
8986   free(cp);\r
8987 }\r
8988 \r
8989 void\r
8990 InterruptChildProcess(ProcRef pr)\r
8991 {\r
8992   ChildProc *cp;\r
8993 \r
8994   cp = (ChildProc *) pr;\r
8995   if (cp == NULL) return;\r
8996   switch (cp->kind) {\r
8997   case CPReal:\r
8998     /* The following doesn't work because the chess program\r
8999        doesn't "have the same console" as WinBoard.  Maybe\r
9000        we could arrange for this even though neither WinBoard\r
9001        nor the chess program uses a console for stdio */\r
9002     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9003     break;\r
9004 \r
9005   case CPComm:\r
9006   case CPSock:\r
9007     /* Can't interrupt */\r
9008     break;\r
9009 \r
9010   case CPRcmd:\r
9011     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9012     break;\r
9013   }\r
9014 }\r
9015 \r
9016 \r
9017 int\r
9018 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9019 {\r
9020   char cmdLine[MSG_SIZ];\r
9021 \r
9022   if (port[0] == NULLCHAR) {\r
9023     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9024   } else {\r
9025     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9026   }\r
9027   return StartChildProcess(cmdLine, "", pr);\r
9028 }\r
9029 \r
9030 \r
9031 /* Code to open TCP sockets */\r
9032 \r
9033 int\r
9034 OpenTCP(char *host, char *port, ProcRef *pr)\r
9035 {\r
9036   ChildProc *cp;\r
9037   int err;\r
9038   SOCKET s;\r
9039   struct sockaddr_in sa, mysa;\r
9040   struct hostent FAR *hp;\r
9041   unsigned short uport;\r
9042   WORD wVersionRequested;\r
9043   WSADATA wsaData;\r
9044 \r
9045   /* Initialize socket DLL */\r
9046   wVersionRequested = MAKEWORD(1, 1);\r
9047   err = WSAStartup(wVersionRequested, &wsaData);\r
9048   if (err != 0) return err;\r
9049 \r
9050   /* Make socket */\r
9051   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9052     err = WSAGetLastError();\r
9053     WSACleanup();\r
9054     return err;\r
9055   }\r
9056 \r
9057   /* Bind local address using (mostly) don't-care values.\r
9058    */\r
9059   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9060   mysa.sin_family = AF_INET;\r
9061   mysa.sin_addr.s_addr = INADDR_ANY;\r
9062   uport = (unsigned short) 0;\r
9063   mysa.sin_port = htons(uport);\r
9064   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9065       == SOCKET_ERROR) {\r
9066     err = WSAGetLastError();\r
9067     WSACleanup();\r
9068     return err;\r
9069   }\r
9070 \r
9071   /* Resolve remote host name */\r
9072   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9073   if (!(hp = gethostbyname(host))) {\r
9074     unsigned int b0, b1, b2, b3;\r
9075 \r
9076     err = WSAGetLastError();\r
9077 \r
9078     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9079       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9080       hp->h_addrtype = AF_INET;\r
9081       hp->h_length = 4;\r
9082       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9083       hp->h_addr_list[0] = (char *) malloc(4);\r
9084       hp->h_addr_list[0][0] = (char) b0;\r
9085       hp->h_addr_list[0][1] = (char) b1;\r
9086       hp->h_addr_list[0][2] = (char) b2;\r
9087       hp->h_addr_list[0][3] = (char) b3;\r
9088     } else {\r
9089       WSACleanup();\r
9090       return err;\r
9091     }\r
9092   }\r
9093   sa.sin_family = hp->h_addrtype;\r
9094   uport = (unsigned short) atoi(port);\r
9095   sa.sin_port = htons(uport);\r
9096   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9097 \r
9098   /* Make connection */\r
9099   if (connect(s, (struct sockaddr *) &sa,\r
9100               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9101     err = WSAGetLastError();\r
9102     WSACleanup();\r
9103     return err;\r
9104   }\r
9105 \r
9106   /* Prepare return value */\r
9107   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9108   cp->kind = CPSock;\r
9109   cp->sock = s;\r
9110   *pr = (ProcRef *) cp;\r
9111 \r
9112   return NO_ERROR;\r
9113 }\r
9114 \r
9115 int\r
9116 OpenCommPort(char *name, ProcRef *pr)\r
9117 {\r
9118   HANDLE h;\r
9119   COMMTIMEOUTS ct;\r
9120   ChildProc *cp;\r
9121   char fullname[MSG_SIZ];\r
9122 \r
9123   if (*name != '\\')\r
9124     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9125   else\r
9126     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9127 \r
9128   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9129                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9130   if (h == (HANDLE) -1) {\r
9131     return GetLastError();\r
9132   }\r
9133   hCommPort = h;\r
9134 \r
9135   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9136 \r
9137   /* Accumulate characters until a 100ms pause, then parse */\r
9138   ct.ReadIntervalTimeout = 100;\r
9139   ct.ReadTotalTimeoutMultiplier = 0;\r
9140   ct.ReadTotalTimeoutConstant = 0;\r
9141   ct.WriteTotalTimeoutMultiplier = 0;\r
9142   ct.WriteTotalTimeoutConstant = 0;\r
9143   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9144 \r
9145   /* Prepare return value */\r
9146   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9147   cp->kind = CPComm;\r
9148   cp->hFrom = h;\r
9149   cp->hTo = h;\r
9150   *pr = (ProcRef *) cp;\r
9151 \r
9152   return NO_ERROR;\r
9153 }\r
9154 \r
9155 int\r
9156 OpenLoopback(ProcRef *pr)\r
9157 {\r
9158   DisplayFatalError(_("Not implemented"), 0, 1);\r
9159   return NO_ERROR;\r
9160 }\r
9161 \r
9162 \r
9163 int\r
9164 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9165 {\r
9166   ChildProc *cp;\r
9167   int err;\r
9168   SOCKET s, s2, s3;\r
9169   struct sockaddr_in sa, mysa;\r
9170   struct hostent FAR *hp;\r
9171   unsigned short uport;\r
9172   WORD wVersionRequested;\r
9173   WSADATA wsaData;\r
9174   int fromPort;\r
9175   char stderrPortStr[MSG_SIZ];\r
9176 \r
9177   /* Initialize socket DLL */\r
9178   wVersionRequested = MAKEWORD(1, 1);\r
9179   err = WSAStartup(wVersionRequested, &wsaData);\r
9180   if (err != 0) return err;\r
9181 \r
9182   /* Resolve remote host name */\r
9183   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9184   if (!(hp = gethostbyname(host))) {\r
9185     unsigned int b0, b1, b2, b3;\r
9186 \r
9187     err = WSAGetLastError();\r
9188 \r
9189     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9190       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9191       hp->h_addrtype = AF_INET;\r
9192       hp->h_length = 4;\r
9193       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9194       hp->h_addr_list[0] = (char *) malloc(4);\r
9195       hp->h_addr_list[0][0] = (char) b0;\r
9196       hp->h_addr_list[0][1] = (char) b1;\r
9197       hp->h_addr_list[0][2] = (char) b2;\r
9198       hp->h_addr_list[0][3] = (char) b3;\r
9199     } else {\r
9200       WSACleanup();\r
9201       return err;\r
9202     }\r
9203   }\r
9204   sa.sin_family = hp->h_addrtype;\r
9205   uport = (unsigned short) 514;\r
9206   sa.sin_port = htons(uport);\r
9207   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9208 \r
9209   /* Bind local socket to unused "privileged" port address\r
9210    */\r
9211   s = INVALID_SOCKET;\r
9212   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9213   mysa.sin_family = AF_INET;\r
9214   mysa.sin_addr.s_addr = INADDR_ANY;\r
9215   for (fromPort = 1023;; fromPort--) {\r
9216     if (fromPort < 0) {\r
9217       WSACleanup();\r
9218       return WSAEADDRINUSE;\r
9219     }\r
9220     if (s == INVALID_SOCKET) {\r
9221       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9222         err = WSAGetLastError();\r
9223         WSACleanup();\r
9224         return err;\r
9225       }\r
9226     }\r
9227     uport = (unsigned short) fromPort;\r
9228     mysa.sin_port = htons(uport);\r
9229     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9230         == SOCKET_ERROR) {\r
9231       err = WSAGetLastError();\r
9232       if (err == WSAEADDRINUSE) continue;\r
9233       WSACleanup();\r
9234       return err;\r
9235     }\r
9236     if (connect(s, (struct sockaddr *) &sa,\r
9237       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9238       err = WSAGetLastError();\r
9239       if (err == WSAEADDRINUSE) {\r
9240         closesocket(s);\r
9241         s = -1;\r
9242         continue;\r
9243       }\r
9244       WSACleanup();\r
9245       return err;\r
9246     }\r
9247     break;\r
9248   }\r
9249 \r
9250   /* Bind stderr local socket to unused "privileged" port address\r
9251    */\r
9252   s2 = INVALID_SOCKET;\r
9253   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9254   mysa.sin_family = AF_INET;\r
9255   mysa.sin_addr.s_addr = INADDR_ANY;\r
9256   for (fromPort = 1023;; fromPort--) {\r
9257     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9258     if (fromPort < 0) {\r
9259       (void) closesocket(s);\r
9260       WSACleanup();\r
9261       return WSAEADDRINUSE;\r
9262     }\r
9263     if (s2 == INVALID_SOCKET) {\r
9264       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9265         err = WSAGetLastError();\r
9266         closesocket(s);\r
9267         WSACleanup();\r
9268         return err;\r
9269       }\r
9270     }\r
9271     uport = (unsigned short) fromPort;\r
9272     mysa.sin_port = htons(uport);\r
9273     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9274         == SOCKET_ERROR) {\r
9275       err = WSAGetLastError();\r
9276       if (err == WSAEADDRINUSE) continue;\r
9277       (void) closesocket(s);\r
9278       WSACleanup();\r
9279       return err;\r
9280     }\r
9281     if (listen(s2, 1) == SOCKET_ERROR) {\r
9282       err = WSAGetLastError();\r
9283       if (err == WSAEADDRINUSE) {\r
9284         closesocket(s2);\r
9285         s2 = INVALID_SOCKET;\r
9286         continue;\r
9287       }\r
9288       (void) closesocket(s);\r
9289       (void) closesocket(s2);\r
9290       WSACleanup();\r
9291       return err;\r
9292     }\r
9293     break;\r
9294   }\r
9295   prevStderrPort = fromPort; // remember port used\r
9296   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9297 \r
9298   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9299     err = WSAGetLastError();\r
9300     (void) closesocket(s);\r
9301     (void) closesocket(s2);\r
9302     WSACleanup();\r
9303     return err;\r
9304   }\r
9305 \r
9306   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9307     err = WSAGetLastError();\r
9308     (void) closesocket(s);\r
9309     (void) closesocket(s2);\r
9310     WSACleanup();\r
9311     return err;\r
9312   }\r
9313   if (*user == NULLCHAR) user = UserName();\r
9314   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9315     err = WSAGetLastError();\r
9316     (void) closesocket(s);\r
9317     (void) closesocket(s2);\r
9318     WSACleanup();\r
9319     return err;\r
9320   }\r
9321   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9322     err = WSAGetLastError();\r
9323     (void) closesocket(s);\r
9324     (void) closesocket(s2);\r
9325     WSACleanup();\r
9326     return err;\r
9327   }\r
9328 \r
9329   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9330     err = WSAGetLastError();\r
9331     (void) closesocket(s);\r
9332     (void) closesocket(s2);\r
9333     WSACleanup();\r
9334     return err;\r
9335   }\r
9336   (void) closesocket(s2);  /* Stop listening */\r
9337 \r
9338   /* Prepare return value */\r
9339   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9340   cp->kind = CPRcmd;\r
9341   cp->sock = s;\r
9342   cp->sock2 = s3;\r
9343   *pr = (ProcRef *) cp;\r
9344 \r
9345   return NO_ERROR;\r
9346 }\r
9347 \r
9348 \r
9349 InputSourceRef\r
9350 AddInputSource(ProcRef pr, int lineByLine,\r
9351                InputCallback func, VOIDSTAR closure)\r
9352 {\r
9353   InputSource *is, *is2 = NULL;\r
9354   ChildProc *cp = (ChildProc *) pr;\r
9355 \r
9356   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9357   is->lineByLine = lineByLine;\r
9358   is->func = func;\r
9359   is->closure = closure;\r
9360   is->second = NULL;\r
9361   is->next = is->buf;\r
9362   if (pr == NoProc) {\r
9363     is->kind = CPReal;\r
9364     consoleInputSource = is;\r
9365   } else {\r
9366     is->kind = cp->kind;\r
9367     /* \r
9368         [AS] Try to avoid a race condition if the thread is given control too early:\r
9369         we create all threads suspended so that the is->hThread variable can be\r
9370         safely assigned, then let the threads start with ResumeThread.\r
9371     */\r
9372     switch (cp->kind) {\r
9373     case CPReal:\r
9374       is->hFile = cp->hFrom;\r
9375       cp->hFrom = NULL; /* now owned by InputThread */\r
9376       is->hThread =\r
9377         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9378                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9379       break;\r
9380 \r
9381     case CPComm:\r
9382       is->hFile = cp->hFrom;\r
9383       cp->hFrom = NULL; /* now owned by InputThread */\r
9384       is->hThread =\r
9385         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9386                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9387       break;\r
9388 \r
9389     case CPSock:\r
9390       is->sock = cp->sock;\r
9391       is->hThread =\r
9392         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9393                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9394       break;\r
9395 \r
9396     case CPRcmd:\r
9397       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9398       *is2 = *is;\r
9399       is->sock = cp->sock;\r
9400       is->second = is2;\r
9401       is2->sock = cp->sock2;\r
9402       is2->second = is2;\r
9403       is->hThread =\r
9404         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9405                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9406       is2->hThread =\r
9407         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9408                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9409       break;\r
9410     }\r
9411 \r
9412     if( is->hThread != NULL ) {\r
9413         ResumeThread( is->hThread );\r
9414     }\r
9415 \r
9416     if( is2 != NULL && is2->hThread != NULL ) {\r
9417         ResumeThread( is2->hThread );\r
9418     }\r
9419   }\r
9420 \r
9421   return (InputSourceRef) is;\r
9422 }\r
9423 \r
9424 void\r
9425 RemoveInputSource(InputSourceRef isr)\r
9426 {\r
9427   InputSource *is;\r
9428 \r
9429   is = (InputSource *) isr;\r
9430   is->hThread = NULL;  /* tell thread to stop */\r
9431   CloseHandle(is->hThread);\r
9432   if (is->second != NULL) {\r
9433     is->second->hThread = NULL;\r
9434     CloseHandle(is->second->hThread);\r
9435   }\r
9436 }\r
9437 \r
9438 int no_wrap(char *message, int count)\r
9439 {\r
9440     ConsoleOutput(message, count, FALSE);\r
9441     return count;\r
9442 }\r
9443 \r
9444 int\r
9445 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9446 {\r
9447   DWORD dOutCount;\r
9448   int outCount = SOCKET_ERROR;\r
9449   ChildProc *cp = (ChildProc *) pr;\r
9450   static OVERLAPPED ovl;\r
9451   static int line = 0;\r
9452 \r
9453   if (pr == NoProc)\r
9454   {\r
9455     if (appData.noJoin || !appData.useInternalWrap)\r
9456       return no_wrap(message, count);\r
9457     else\r
9458     {\r
9459       int width = get_term_width();\r
9460       int len = wrap(NULL, message, count, width, &line);\r
9461       char *msg = malloc(len);\r
9462       int dbgchk;\r
9463 \r
9464       if (!msg)\r
9465         return no_wrap(message, count);\r
9466       else\r
9467       {\r
9468         dbgchk = wrap(msg, message, count, width, &line);\r
9469         if (dbgchk != len && appData.debugMode)\r
9470             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9471         ConsoleOutput(msg, len, FALSE);\r
9472         free(msg);\r
9473         return len;\r
9474       }\r
9475     }\r
9476   }\r
9477 \r
9478   if (ovl.hEvent == NULL) {\r
9479     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9480   }\r
9481   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9482 \r
9483   switch (cp->kind) {\r
9484   case CPSock:\r
9485   case CPRcmd:\r
9486     outCount = send(cp->sock, message, count, 0);\r
9487     if (outCount == SOCKET_ERROR) {\r
9488       *outError = WSAGetLastError();\r
9489     } else {\r
9490       *outError = NO_ERROR;\r
9491     }\r
9492     break;\r
9493 \r
9494   case CPReal:\r
9495     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9496                   &dOutCount, NULL)) {\r
9497       *outError = NO_ERROR;\r
9498       outCount = (int) dOutCount;\r
9499     } else {\r
9500       *outError = GetLastError();\r
9501     }\r
9502     break;\r
9503 \r
9504   case CPComm:\r
9505     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9506                             &dOutCount, &ovl);\r
9507     if (*outError == NO_ERROR) {\r
9508       outCount = (int) dOutCount;\r
9509     }\r
9510     break;\r
9511   }\r
9512   return outCount;\r
9513 }\r
9514 \r
9515 int\r
9516 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9517                        long msdelay)\r
9518 {\r
9519   /* Ignore delay, not implemented for WinBoard */\r
9520   return OutputToProcess(pr, message, count, outError);\r
9521 }\r
9522 \r
9523 \r
9524 void\r
9525 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9526                         char *buf, int count, int error)\r
9527 {\r
9528   DisplayFatalError(_("Not implemented"), 0, 1);\r
9529 }\r
9530 \r
9531 /* see wgamelist.c for Game List functions */\r
9532 /* see wedittags.c for Edit Tags functions */\r
9533 \r
9534 \r
9535 VOID\r
9536 ICSInitScript()\r
9537 {\r
9538   FILE *f;\r
9539   char buf[MSG_SIZ];\r
9540   char *dummy;\r
9541 \r
9542   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9543     f = fopen(buf, "r");\r
9544     if (f != NULL) {\r
9545       ProcessICSInitScript(f);\r
9546       fclose(f);\r
9547     }\r
9548   }\r
9549 }\r
9550 \r
9551 \r
9552 VOID\r
9553 StartAnalysisClock()\r
9554 {\r
9555   if (analysisTimerEvent) return;\r
9556   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9557                                         (UINT) 2000, NULL);\r
9558 }\r
9559 \r
9560 VOID\r
9561 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9562 {\r
9563   highlightInfo.sq[0].x = fromX;\r
9564   highlightInfo.sq[0].y = fromY;\r
9565   highlightInfo.sq[1].x = toX;\r
9566   highlightInfo.sq[1].y = toY;\r
9567 }\r
9568 \r
9569 VOID\r
9570 ClearHighlights()\r
9571 {\r
9572   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9573     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9574 }\r
9575 \r
9576 VOID\r
9577 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9578 {\r
9579   premoveHighlightInfo.sq[0].x = fromX;\r
9580   premoveHighlightInfo.sq[0].y = fromY;\r
9581   premoveHighlightInfo.sq[1].x = toX;\r
9582   premoveHighlightInfo.sq[1].y = toY;\r
9583 }\r
9584 \r
9585 VOID\r
9586 ClearPremoveHighlights()\r
9587 {\r
9588   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9589     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9590 }\r
9591 \r
9592 VOID\r
9593 ShutDownFrontEnd()\r
9594 {\r
9595   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9596   DeleteClipboardTempFiles();\r
9597 }\r
9598 \r
9599 void\r
9600 BoardToTop()\r
9601 {\r
9602     if (IsIconic(hwndMain))\r
9603       ShowWindow(hwndMain, SW_RESTORE);\r
9604 \r
9605     SetActiveWindow(hwndMain);\r
9606 }\r
9607 \r
9608 /*\r
9609  * Prototypes for animation support routines\r
9610  */\r
9611 static void ScreenSquare(int column, int row, POINT * pt);\r
9612 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9613      POINT frames[], int * nFrames);\r
9614 \r
9615 \r
9616 void\r
9617 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9618 {       // [HGM] atomic: animate blast wave\r
9619         int i;\r
9620 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9621         explodeInfo.fromX = fromX;\r
9622         explodeInfo.fromY = fromY;\r
9623         explodeInfo.toX = toX;\r
9624         explodeInfo.toY = toY;\r
9625         for(i=1; i<nFrames; i++) {\r
9626             explodeInfo.radius = (i*180)/(nFrames-1);\r
9627             DrawPosition(FALSE, NULL);\r
9628             Sleep(appData.animSpeed);\r
9629         }\r
9630         explodeInfo.radius = 0;\r
9631         DrawPosition(TRUE, NULL);\r
9632 }\r
9633 \r
9634 #define kFactor 4\r
9635 \r
9636 void\r
9637 AnimateMove(board, fromX, fromY, toX, toY)\r
9638      Board board;\r
9639      int fromX;\r
9640      int fromY;\r
9641      int toX;\r
9642      int toY;\r
9643 {\r
9644   ChessSquare piece;\r
9645   POINT start, finish, mid;\r
9646   POINT frames[kFactor * 2 + 1];\r
9647   int nFrames, n;\r
9648 \r
9649   if (!appData.animate) return;\r
9650   if (doingSizing) return;\r
9651   if (fromY < 0 || fromX < 0) return;\r
9652   piece = board[fromY][fromX];\r
9653   if (piece >= EmptySquare) return;\r
9654 \r
9655   ScreenSquare(fromX, fromY, &start);\r
9656   ScreenSquare(toX, toY, &finish);\r
9657 \r
9658   /* All pieces except knights move in straight line */\r
9659   if (piece != WhiteKnight && piece != BlackKnight) {\r
9660     mid.x = start.x + (finish.x - start.x) / 2;\r
9661     mid.y = start.y + (finish.y - start.y) / 2;\r
9662   } else {\r
9663     /* Knight: make diagonal movement then straight */\r
9664     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9665        mid.x = start.x + (finish.x - start.x) / 2;\r
9666        mid.y = finish.y;\r
9667      } else {\r
9668        mid.x = finish.x;\r
9669        mid.y = start.y + (finish.y - start.y) / 2;\r
9670      }\r
9671   }\r
9672   \r
9673   /* Don't use as many frames for very short moves */\r
9674   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9675     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9676   else\r
9677     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9678 \r
9679   animInfo.from.x = fromX;\r
9680   animInfo.from.y = fromY;\r
9681   animInfo.to.x = toX;\r
9682   animInfo.to.y = toY;\r
9683   animInfo.lastpos = start;\r
9684   animInfo.piece = piece;\r
9685   for (n = 0; n < nFrames; n++) {\r
9686     animInfo.pos = frames[n];\r
9687     DrawPosition(FALSE, NULL);\r
9688     animInfo.lastpos = animInfo.pos;\r
9689     Sleep(appData.animSpeed);\r
9690   }\r
9691   animInfo.pos = finish;\r
9692   DrawPosition(FALSE, NULL);\r
9693   animInfo.piece = EmptySquare;\r
9694   if(gameInfo.variant == VariantAtomic && \r
9695      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9696         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9697 }\r
9698 \r
9699 /*      Convert board position to corner of screen rect and color       */\r
9700 \r
9701 static void\r
9702 ScreenSquare(column, row, pt)\r
9703      int column; int row; POINT * pt;\r
9704 {\r
9705   if (flipView) {\r
9706     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9707     pt->y = lineGap + row * (squareSize + lineGap);\r
9708   } else {\r
9709     pt->x = lineGap + column * (squareSize + lineGap);\r
9710     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9711   }\r
9712 }\r
9713 \r
9714 /*      Generate a series of frame coords from start->mid->finish.\r
9715         The movement rate doubles until the half way point is\r
9716         reached, then halves back down to the final destination,\r
9717         which gives a nice slow in/out effect. The algorithmn\r
9718         may seem to generate too many intermediates for short\r
9719         moves, but remember that the purpose is to attract the\r
9720         viewers attention to the piece about to be moved and\r
9721         then to where it ends up. Too few frames would be less\r
9722         noticeable.                                             */\r
9723 \r
9724 static void\r
9725 Tween(start, mid, finish, factor, frames, nFrames)\r
9726      POINT * start; POINT * mid;\r
9727      POINT * finish; int factor;\r
9728      POINT frames[]; int * nFrames;\r
9729 {\r
9730   int n, fraction = 1, count = 0;\r
9731 \r
9732   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9733   for (n = 0; n < factor; n++)\r
9734     fraction *= 2;\r
9735   for (n = 0; n < factor; n++) {\r
9736     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9737     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9738     count ++;\r
9739     fraction = fraction / 2;\r
9740   }\r
9741   \r
9742   /* Midpoint */\r
9743   frames[count] = *mid;\r
9744   count ++;\r
9745   \r
9746   /* Slow out, stepping 1/2, then 1/4, ... */\r
9747   fraction = 2;\r
9748   for (n = 0; n < factor; n++) {\r
9749     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9750     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9751     count ++;\r
9752     fraction = fraction * 2;\r
9753   }\r
9754   *nFrames = count;\r
9755 }\r
9756 \r
9757 void\r
9758 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9759 {\r
9760     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9761 \r
9762     EvalGraphSet( first, last, current, pvInfoList );\r
9763 }\r