f26312458b5ce3a16d70a6c62380061db2b72a09
[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, 2011, 2012 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 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\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   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][42] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
257   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
318   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
319   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
322   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
323 { DLG_MoveHistory }, \r
324 { DLG_EvalGraph }, \r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
328   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
329   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
330   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
332   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
333   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
334 { 0 }\r
335 };\r
336 \r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
338 static int lastChecked;\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
340 extern int tinyLayout;\r
341 extern char * menuBarText[][10];\r
342 \r
343 void\r
344 LoadLanguageFile(char *name)\r
345 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
346     FILE *f;\r
347     int i=0, j=0, n=0, k;\r
348     char buf[MSG_SIZ];\r
349 \r
350     if(!name || name[0] == NULLCHAR) return;\r
351       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
352     appData.language = oldLanguage;\r
353     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
354     if((f = fopen(buf, "r")) == NULL) return;\r
355     while((k = fgetc(f)) != EOF) {\r
356         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
357         languageBuf[i] = k;\r
358         if(k == '\n') {\r
359             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
360                 char *p;\r
361                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
362                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
363                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
364                         english[j] = languageBuf + n + 1; *p = 0;\r
365                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
367                     }\r
368                 }\r
369             }\r
370             n = i + 1;\r
371         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
372             switch(k) {\r
373               case 'n': k = '\n'; break;\r
374               case 'r': k = '\r'; break;\r
375               case 't': k = '\t'; break;\r
376             }\r
377             languageBuf[--i] = k;\r
378         }\r
379         i++;\r
380     }\r
381     fclose(f);\r
382     barbaric = (j != 0);\r
383     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
384 }\r
385 \r
386 char *\r
387 T_(char *s)\r
388 {   // return the translation of the given string\r
389     // efficiency can be improved a lot...\r
390     int i=0;\r
391     static char buf[MSG_SIZ];\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
393     if(!barbaric) return s;\r
394     if(!s) return ""; // sanity\r
395     while(english[i]) {\r
396         if(!strcmp(s, english[i])) return foreign[i];\r
397         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
398             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
399             return buf;\r
400         }\r
401         i++;\r
402     }\r
403     return s;\r
404 }\r
405 \r
406 void\r
407 Translate(HWND hDlg, int dialogID)\r
408 {   // translate all text items in the given dialog\r
409     int i=0, j, k;\r
410     char buf[MSG_SIZ], *s;\r
411     if(!barbaric) return;\r
412     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
413     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
414     GetWindowText( hDlg, buf, MSG_SIZ );\r
415     s = T_(buf);\r
416     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
417     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
418         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
419         if(strlen(buf) == 0) continue;\r
420         s = T_(buf);\r
421         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
422     }\r
423 }\r
424 \r
425 HMENU\r
426 TranslateOneMenu(int i, HMENU subMenu)\r
427 {\r
428     int j;\r
429     static MENUITEMINFO info;\r
430 \r
431     info.cbSize = sizeof(MENUITEMINFO);\r
432     info.fMask = MIIM_STATE | MIIM_TYPE;\r
433           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
434             char buf[MSG_SIZ];\r
435             info.dwTypeData = buf;\r
436             info.cch = sizeof(buf);\r
437             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
438             if(i < 10) {\r
439                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
440                 else menuText[i][j] = strdup(buf); // remember original on first change\r
441             }\r
442             if(buf[0] == NULLCHAR) continue;\r
443             info.dwTypeData = T_(buf);\r
444             info.cch = strlen(buf)+1;\r
445             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
446           }\r
447     return subMenu;\r
448 }\r
449 \r
450 void\r
451 TranslateMenus(int addLanguage)\r
452 {\r
453     int i;\r
454     WIN32_FIND_DATA fileData;\r
455     HANDLE hFind;\r
456 #define IDM_English 1970\r
457     if(1) {\r
458         HMENU mainMenu = GetMenu(hwndMain);\r
459         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
460           HMENU subMenu = GetSubMenu(mainMenu, i);\r
461           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
462                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
463           TranslateOneMenu(i, subMenu);\r
464         }\r
465         DrawMenuBar(hwndMain);\r
466     }\r
467 \r
468     if(!addLanguage) return;\r
469     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
470         HMENU mainMenu = GetMenu(hwndMain);\r
471         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
472         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
473         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
474         i = 0; lastChecked = IDM_English;\r
475         do {\r
476             char *p, *q = fileData.cFileName;\r
477             int checkFlag = MF_UNCHECKED;\r
478             languageFile[i] = strdup(q);\r
479             if(barbaric && !strcmp(oldLanguage, q)) {\r
480                 checkFlag = MF_CHECKED;\r
481                 lastChecked = IDM_English + i + 1;\r
482                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
483             }\r
484             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
485             p = strstr(fileData.cFileName, ".lng");\r
486             if(p) *p = 0;\r
487             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
488         } while(FindNextFile(hFind, &fileData));\r
489         FindClose(hFind);\r
490     }\r
491 }\r
492 \r
493 #endif\r
494 \r
495 #define IDM_RecentEngines 3000\r
496 \r
497 void\r
498 RecentEngineMenu (char *s)\r
499 {\r
500     if(appData.icsActive) return;\r
501     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
502         HMENU mainMenu = GetMenu(hwndMain);\r
503         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
504         int i=IDM_RecentEngines;\r
505         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
506         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
507         while(*s) {\r
508           char *p = strchr(s, '\n');\r
509           if(p == NULL) return; // malformed!\r
510           *p = NULLCHAR;\r
511           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
512           *p = '\n';\r
513           s = p+1;\r
514         }\r
515     }\r
516 }\r
517 \r
518 \r
519 typedef struct {\r
520   char *name;\r
521   int squareSize;\r
522   int lineGap;\r
523   int smallLayout;\r
524   int tinyLayout;\r
525   int cliWidth, cliHeight;\r
526 } SizeInfo;\r
527 \r
528 SizeInfo sizeInfo[] = \r
529 {\r
530   { "tiny",     21, 0, 1, 1, 0, 0 },\r
531   { "teeny",    25, 1, 1, 1, 0, 0 },\r
532   { "dinky",    29, 1, 1, 1, 0, 0 },\r
533   { "petite",   33, 1, 1, 1, 0, 0 },\r
534   { "slim",     37, 2, 1, 0, 0, 0 },\r
535   { "small",    40, 2, 1, 0, 0, 0 },\r
536   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
537   { "middling", 49, 2, 0, 0, 0, 0 },\r
538   { "average",  54, 2, 0, 0, 0, 0 },\r
539   { "moderate", 58, 3, 0, 0, 0, 0 },\r
540   { "medium",   64, 3, 0, 0, 0, 0 },\r
541   { "bulky",    72, 3, 0, 0, 0, 0 },\r
542   { "large",    80, 3, 0, 0, 0, 0 },\r
543   { "big",      87, 3, 0, 0, 0, 0 },\r
544   { "huge",     95, 3, 0, 0, 0, 0 },\r
545   { "giant",    108, 3, 0, 0, 0, 0 },\r
546   { "colossal", 116, 4, 0, 0, 0, 0 },\r
547   { "titanic",  129, 4, 0, 0, 0, 0 },\r
548   { NULL, 0, 0, 0, 0, 0, 0 }\r
549 };\r
550 \r
551 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
552 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
553 {\r
554   { 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), MF(GAMELIST_FONT_ALL) },\r
555   { 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), MF(GAMELIST_FONT_ALL) },\r
556   { 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), MF(GAMELIST_FONT_ALL) },\r
557   { 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), MF(GAMELIST_FONT_ALL) },\r
558   { 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), MF(GAMELIST_FONT_ALL) },\r
559   { 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), MF(GAMELIST_FONT_ALL) },\r
560   { 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), MF(GAMELIST_FONT_ALL) },\r
561   { 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), MF(GAMELIST_FONT_ALL) },\r
562   { 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), MF(GAMELIST_FONT_ALL) },\r
563   { 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), MF(GAMELIST_FONT_ALL) },\r
564   { 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),  MF(GAMELIST_FONT_ALL) },\r
565   { 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),  MF(GAMELIST_FONT_ALL) },\r
566   { 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),  MF(GAMELIST_FONT_ALL) },\r
567   { 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),  MF(GAMELIST_FONT_ALL) },\r
568   { 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), MF(GAMELIST_FONT_ALL) },\r
569   { 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), MF(GAMELIST_FONT_ALL) },\r
570   { 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), MF (GAMELIST_FONT_ALL) },\r
571   { 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), MF(GAMELIST_FONT_ALL) },\r
572 };\r
573 \r
574 MyFont *font[NUM_SIZES][NUM_FONTS];\r
575 \r
576 typedef struct {\r
577   char *label;\r
578   int id;\r
579   HWND hwnd;\r
580   WNDPROC wndproc;\r
581 } MyButtonDesc;\r
582 \r
583 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
584 #define N_BUTTONS 5\r
585 \r
586 MyButtonDesc buttonDesc[N_BUTTONS] =\r
587 {\r
588   {"<<", IDM_ToStart, NULL, NULL},\r
589   {"<", IDM_Backward, NULL, NULL},\r
590   {"P", IDM_Pause, NULL, NULL},\r
591   {">", IDM_Forward, NULL, NULL},\r
592   {">>", IDM_ToEnd, NULL, NULL},\r
593 };\r
594 \r
595 int tinyLayout = 0, smallLayout = 0;\r
596 #define MENU_BAR_ITEMS 9\r
597 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
598   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
599   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
600 };\r
601 \r
602 \r
603 MySound sounds[(int)NSoundClasses];\r
604 MyTextAttribs textAttribs[(int)NColorClasses];\r
605 \r
606 MyColorizeAttribs colorizeAttribs[] = {\r
607   { (COLORREF)0, 0, N_("Shout Text") },\r
608   { (COLORREF)0, 0, N_("SShout/CShout") },\r
609   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
610   { (COLORREF)0, 0, N_("Channel Text") },\r
611   { (COLORREF)0, 0, N_("Kibitz Text") },\r
612   { (COLORREF)0, 0, N_("Tell Text") },\r
613   { (COLORREF)0, 0, N_("Challenge Text") },\r
614   { (COLORREF)0, 0, N_("Request Text") },\r
615   { (COLORREF)0, 0, N_("Seek Text") },\r
616   { (COLORREF)0, 0, N_("Normal Text") },\r
617   { (COLORREF)0, 0, N_("None") }\r
618 };\r
619 \r
620 \r
621 \r
622 static char *commentTitle;\r
623 static char *commentText;\r
624 static int commentIndex;\r
625 static Boolean editComment = FALSE;\r
626 \r
627 \r
628 char errorTitle[MSG_SIZ];\r
629 char errorMessage[2*MSG_SIZ];\r
630 HWND errorDialog = NULL;\r
631 BOOLEAN moveErrorMessageUp = FALSE;\r
632 BOOLEAN consoleEcho = TRUE;\r
633 CHARFORMAT consoleCF;\r
634 COLORREF consoleBackgroundColor;\r
635 \r
636 char *programVersion;\r
637 \r
638 #define CPReal 1\r
639 #define CPComm 2\r
640 #define CPSock 3\r
641 #define CPRcmd 4\r
642 typedef int CPKind;\r
643 \r
644 typedef struct {\r
645   CPKind kind;\r
646   HANDLE hProcess;\r
647   DWORD pid;\r
648   HANDLE hTo;\r
649   HANDLE hFrom;\r
650   SOCKET sock;\r
651   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
652 } ChildProc;\r
653 \r
654 #define INPUT_SOURCE_BUF_SIZE 4096\r
655 \r
656 typedef struct _InputSource {\r
657   CPKind kind;\r
658   HANDLE hFile;\r
659   SOCKET sock;\r
660   int lineByLine;\r
661   HANDLE hThread;\r
662   DWORD id;\r
663   char buf[INPUT_SOURCE_BUF_SIZE];\r
664   char *next;\r
665   DWORD count;\r
666   int error;\r
667   InputCallback func;\r
668   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
669   VOIDSTAR closure;\r
670 } InputSource;\r
671 \r
672 InputSource *consoleInputSource;\r
673 \r
674 DCB dcb;\r
675 \r
676 /* forward */\r
677 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
678 VOID ConsoleCreate();\r
679 LRESULT CALLBACK\r
680   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
681 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
682 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
683 VOID ParseCommSettings(char *arg, DCB *dcb);\r
684 LRESULT CALLBACK\r
685   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
686 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
687 void ParseIcsTextMenu(char *icsTextMenuString);\r
688 VOID PopUpNameDialog(char firstchar);\r
689 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
690 \r
691 /* [AS] */\r
692 int NewGameFRC();\r
693 int GameListOptions();\r
694 \r
695 int dummy; // [HGM] for obsolete args\r
696 \r
697 HWND hwndMain = NULL;        /* root window*/\r
698 HWND hwndConsole = NULL;\r
699 HWND commentDialog = NULL;\r
700 HWND moveHistoryDialog = NULL;\r
701 HWND evalGraphDialog = NULL;\r
702 HWND engineOutputDialog = NULL;\r
703 HWND gameListDialog = NULL;\r
704 HWND editTagsDialog = NULL;\r
705 \r
706 int commentUp = FALSE;\r
707 \r
708 WindowPlacement wpMain;\r
709 WindowPlacement wpConsole;\r
710 WindowPlacement wpComment;\r
711 WindowPlacement wpMoveHistory;\r
712 WindowPlacement wpEvalGraph;\r
713 WindowPlacement wpEngineOutput;\r
714 WindowPlacement wpGameList;\r
715 WindowPlacement wpTags;\r
716 \r
717 VOID EngineOptionsPopup(); // [HGM] settings\r
718 \r
719 VOID GothicPopUp(char *title, VariantClass variant);\r
720 /*\r
721  * Setting "frozen" should disable all user input other than deleting\r
722  * the window.  We do this while engines are initializing themselves.\r
723  */\r
724 static int frozen = 0;\r
725 static int oldMenuItemState[MENU_BAR_ITEMS];\r
726 void FreezeUI()\r
727 {\r
728   HMENU hmenu;\r
729   int i;\r
730 \r
731   if (frozen) return;\r
732   frozen = 1;\r
733   hmenu = GetMenu(hwndMain);\r
734   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
735     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
736   }\r
737   DrawMenuBar(hwndMain);\r
738 }\r
739 \r
740 /* Undo a FreezeUI */\r
741 void ThawUI()\r
742 {\r
743   HMENU hmenu;\r
744   int i;\r
745 \r
746   if (!frozen) return;\r
747   frozen = 0;\r
748   hmenu = GetMenu(hwndMain);\r
749   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
750     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
751   }\r
752   DrawMenuBar(hwndMain);\r
753 }\r
754 \r
755 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
756 \r
757 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
758 #ifdef JAWS\r
759 #include "jaws.c"\r
760 #else\r
761 #define JAWS_INIT\r
762 #define JAWS_ARGS\r
763 #define JAWS_ALT_INTERCEPT\r
764 #define JAWS_KBUP_NAVIGATION\r
765 #define JAWS_KBDOWN_NAVIGATION\r
766 #define JAWS_MENU_ITEMS\r
767 #define JAWS_SILENCE\r
768 #define JAWS_REPLAY\r
769 #define JAWS_ACCEL\r
770 #define JAWS_COPYRIGHT\r
771 #define JAWS_DELETE(X) X\r
772 #define SAYMACHINEMOVE()\r
773 #define SAY(X)\r
774 #endif\r
775 \r
776 /*---------------------------------------------------------------------------*\\r
777  *\r
778  * WinMain\r
779  *\r
780 \*---------------------------------------------------------------------------*/\r
781 \r
782 int APIENTRY\r
783 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
784         LPSTR lpCmdLine, int nCmdShow)\r
785 {\r
786   MSG msg;\r
787   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
788 //  INITCOMMONCONTROLSEX ex;\r
789 \r
790   debugFP = stderr;\r
791 \r
792   LoadLibrary("RICHED32.DLL");\r
793   consoleCF.cbSize = sizeof(CHARFORMAT);\r
794 \r
795   if (!InitApplication(hInstance)) {\r
796     return (FALSE);\r
797   }\r
798   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
799     return (FALSE);\r
800   }\r
801 \r
802   JAWS_INIT\r
803   TranslateMenus(1);\r
804 \r
805 //  InitCommonControlsEx(&ex);\r
806   InitCommonControls();\r
807 \r
808   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
809   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
810   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
811 \r
812   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
813 \r
814   while (GetMessage(&msg, /* message structure */\r
815                     NULL, /* handle of window receiving the message */\r
816                     0,    /* lowest message to examine */\r
817                     0))   /* highest message to examine */\r
818     {\r
819 \r
820       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
821         // [HGM] navigate: switch between all windows with tab\r
822         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
823         int i, currentElement = 0;\r
824 \r
825         // first determine what element of the chain we come from (if any)\r
826         if(appData.icsActive) {\r
827             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
828             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
829         }\r
830         if(engineOutputDialog && EngineOutputIsUp()) {\r
831             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
832             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
833         }\r
834         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
835             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
836         }\r
837         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
838         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
839         if(msg.hwnd == e1)                 currentElement = 2; else\r
840         if(msg.hwnd == e2)                 currentElement = 3; else\r
841         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
842         if(msg.hwnd == mh)                currentElement = 4; else\r
843         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
844         if(msg.hwnd == hText)  currentElement = 5; else\r
845         if(msg.hwnd == hInput) currentElement = 6; else\r
846         for (i = 0; i < N_BUTTONS; i++) {\r
847             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
848         }\r
849 \r
850         // determine where to go to\r
851         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
852           do {\r
853             currentElement = (currentElement + direction) % 7;\r
854             switch(currentElement) {\r
855                 case 0:\r
856                   h = hwndMain; break; // passing this case always makes the loop exit\r
857                 case 1:\r
858                   h = buttonDesc[0].hwnd; break; // could be NULL\r
859                 case 2:\r
860                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
861                   h = e1; break;\r
862                 case 3:\r
863                   if(!EngineOutputIsUp()) continue;\r
864                   h = e2; break;\r
865                 case 4:\r
866                   if(!MoveHistoryIsUp()) continue;\r
867                   h = mh; break;\r
868 //              case 6: // input to eval graph does not seem to get here!\r
869 //                if(!EvalGraphIsUp()) continue;\r
870 //                h = evalGraphDialog; break;\r
871                 case 5:\r
872                   if(!appData.icsActive) continue;\r
873                   SAY("display");\r
874                   h = hText; break;\r
875                 case 6:\r
876                   if(!appData.icsActive) continue;\r
877                   SAY("input");\r
878                   h = hInput; break;\r
879             }\r
880           } while(h == 0);\r
881 \r
882           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
883           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
884           SetFocus(h);\r
885 \r
886           continue; // this message now has been processed\r
887         }\r
888       }\r
889 \r
890       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
891           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
892           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
893           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
894           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
895           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
896           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
897           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
898           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
899           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
900         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
901         for(i=0; i<MAX_CHAT; i++) \r
902             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
903                 done = 1; break;\r
904         }\r
905         if(done) continue; // [HGM] chat: end patch\r
906         TranslateMessage(&msg); /* Translates virtual key codes */\r
907         DispatchMessage(&msg);  /* Dispatches message to window */\r
908       }\r
909     }\r
910 \r
911 \r
912   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
913 }\r
914 \r
915 /*---------------------------------------------------------------------------*\\r
916  *\r
917  * Initialization functions\r
918  *\r
919 \*---------------------------------------------------------------------------*/\r
920 \r
921 void\r
922 SetUserLogo()\r
923 {   // update user logo if necessary\r
924     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
925 \r
926     if(appData.autoLogo) {\r
927           curName = UserName();\r
928           if(strcmp(curName, oldUserName)) {\r
929                 GetCurrentDirectory(MSG_SIZ, dir);\r
930                 SetCurrentDirectory(installDir);\r
931                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
932                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
933                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
934                 if(userLogo == NULL)\r
935                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
936                 SetCurrentDirectory(dir); /* return to prev directory */\r
937           }\r
938     }\r
939 }\r
940 \r
941 BOOL\r
942 InitApplication(HINSTANCE hInstance)\r
943 {\r
944   WNDCLASS wc;\r
945 \r
946   /* Fill in window class structure with parameters that describe the */\r
947   /* main window. */\r
948 \r
949   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
950   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
951   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
952   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
953   wc.hInstance     = hInstance;         /* Owner of this class */\r
954   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
955   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
956   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
957   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
958   wc.lpszClassName = szAppName;                 /* Name to register as */\r
959 \r
960   /* Register the window class and return success/failure code. */\r
961   if (!RegisterClass(&wc)) return FALSE;\r
962 \r
963   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
964   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
965   wc.cbClsExtra    = 0;\r
966   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
967   wc.hInstance     = hInstance;\r
968   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
969   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
970   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
971   wc.lpszMenuName  = NULL;\r
972   wc.lpszClassName = szConsoleName;\r
973 \r
974   if (!RegisterClass(&wc)) return FALSE;\r
975   return TRUE;\r
976 }\r
977 \r
978 \r
979 /* Set by InitInstance, used by EnsureOnScreen */\r
980 int screenHeight, screenWidth;\r
981 \r
982 void\r
983 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
984 {\r
985 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
986   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
987   if (*x > screenWidth - 32) *x = 0;\r
988   if (*y > screenHeight - 32) *y = 0;\r
989   if (*x < minX) *x = minX;\r
990   if (*y < minY) *y = minY;\r
991 }\r
992 \r
993 VOID\r
994 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
995 {\r
996   char buf[MSG_SIZ], dir[MSG_SIZ];\r
997   GetCurrentDirectory(MSG_SIZ, dir);\r
998   SetCurrentDirectory(installDir);\r
999   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1000       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1001 \r
1002       if (cps->programLogo == NULL && appData.debugMode) {\r
1003           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1004       }\r
1005   } else if(appData.autoLogo) {\r
1006       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1007         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1008         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1009       } else\r
1010       if(appData.directory[n] && appData.directory[n][0]) {\r
1011         SetCurrentDirectory(appData.directory[n]);\r
1012         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1013       }\r
1014   }\r
1015   SetCurrentDirectory(dir); /* return to prev directory */\r
1016 }\r
1017 \r
1018 VOID\r
1019 InitTextures()\r
1020 {\r
1021   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1022   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1023   \r
1024   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1025       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1026       liteBackTextureMode = appData.liteBackTextureMode;\r
1027 \r
1028       if (liteBackTexture == NULL && appData.debugMode) {\r
1029           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1030       }\r
1031   }\r
1032   \r
1033   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1034       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035       darkBackTextureMode = appData.darkBackTextureMode;\r
1036 \r
1037       if (darkBackTexture == NULL && appData.debugMode) {\r
1038           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1039       }\r
1040   }\r
1041 }\r
1042 \r
1043 BOOL\r
1044 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1045 {\r
1046   HWND hwnd; /* Main window handle. */\r
1047   int ibs;\r
1048   WINDOWPLACEMENT wp;\r
1049   char *filepart;\r
1050 \r
1051   hInst = hInstance;    /* Store instance handle in our global variable */\r
1052   programName = szAppName;\r
1053 \r
1054   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1055     *filepart = NULLCHAR;\r
1056   } else {\r
1057     GetCurrentDirectory(MSG_SIZ, installDir);\r
1058   }\r
1059   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1060   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1061   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1062   /* xboard, and older WinBoards, controlled the move sound with the\r
1063      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1064      always turn the option on (so that the backend will call us),\r
1065      then let the user turn the sound off by setting it to silence if\r
1066      desired.  To accommodate old winboard.ini files saved by old\r
1067      versions of WinBoard, we also turn off the sound if the option\r
1068      was initially set to false. [HGM] taken out of InitAppData */\r
1069   if (!appData.ringBellAfterMoves) {\r
1070     sounds[(int)SoundMove].name = strdup("");\r
1071     appData.ringBellAfterMoves = TRUE;\r
1072   }\r
1073   if (appData.debugMode) {\r
1074     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1075     setbuf(debugFP, NULL);\r
1076   }\r
1077 \r
1078   LoadLanguageFile(appData.language);\r
1079 \r
1080   InitBackEnd1();\r
1081 \r
1082 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1083 //  InitEngineUCI( installDir, &second );\r
1084 \r
1085   /* Create a main window for this application instance. */\r
1086   hwnd = CreateWindow(szAppName, szTitle,\r
1087                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1088                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1089                       NULL, NULL, hInstance, NULL);\r
1090   hwndMain = hwnd;\r
1091 \r
1092   /* If window could not be created, return "failure" */\r
1093   if (!hwnd) {\r
1094     return (FALSE);\r
1095   }\r
1096 \r
1097   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1098   LoadLogo(&first, 0, FALSE);\r
1099   LoadLogo(&second, 1, appData.icsActive);\r
1100 \r
1101   SetUserLogo();\r
1102 \r
1103   iconWhite = LoadIcon(hInstance, "icon_white");\r
1104   iconBlack = LoadIcon(hInstance, "icon_black");\r
1105   iconCurrent = iconWhite;\r
1106   InitDrawingColors();\r
1107   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1108   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1109   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1110     /* Compute window size for each board size, and use the largest\r
1111        size that fits on this screen as the default. */\r
1112     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1113     if (boardSize == (BoardSize)-1 &&\r
1114         winH <= screenHeight\r
1115            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1116         && winW <= screenWidth) {\r
1117       boardSize = (BoardSize)ibs;\r
1118     }\r
1119   }\r
1120 \r
1121   InitDrawingSizes(boardSize, 0);\r
1122   RecentEngineMenu(appData.recentEngineList);\r
1123   InitMenuChecks();\r
1124   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1125 \r
1126   /* [AS] Load textures if specified */\r
1127   InitTextures();\r
1128 \r
1129   mysrandom( (unsigned) time(NULL) );\r
1130 \r
1131   /* [AS] Restore layout */\r
1132   if( wpMoveHistory.visible ) {\r
1133       MoveHistoryPopUp();\r
1134   }\r
1135 \r
1136   if( wpEvalGraph.visible ) {\r
1137       EvalGraphPopUp();\r
1138   }\r
1139 \r
1140   if( wpEngineOutput.visible ) {\r
1141       EngineOutputPopUp();\r
1142   }\r
1143 \r
1144   /* Make the window visible; update its client area; and return "success" */\r
1145   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1146   wp.length = sizeof(WINDOWPLACEMENT);\r
1147   wp.flags = 0;\r
1148   wp.showCmd = nCmdShow;\r
1149   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1150   wp.rcNormalPosition.left = wpMain.x;\r
1151   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1152   wp.rcNormalPosition.top = wpMain.y;\r
1153   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1154   SetWindowPlacement(hwndMain, &wp);\r
1155 \r
1156   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1157 \r
1158   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1159                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1160 \r
1161   if (hwndConsole) {\r
1162 #if AOT_CONSOLE\r
1163     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1164                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1165 #endif\r
1166     ShowWindow(hwndConsole, nCmdShow);\r
1167     SetActiveWindow(hwndConsole);\r
1168   }\r
1169   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1170   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1171 \r
1172   return TRUE;\r
1173 \r
1174 }\r
1175 \r
1176 VOID\r
1177 InitMenuChecks()\r
1178 {\r
1179   HMENU hmenu = GetMenu(hwndMain);\r
1180 \r
1181   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1182                         MF_BYCOMMAND|((appData.icsActive &&\r
1183                                        *appData.icsCommPort != NULLCHAR) ?\r
1184                                       MF_ENABLED : MF_GRAYED));\r
1185   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1186                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1187                                      MF_CHECKED : MF_UNCHECKED));\r
1188 }\r
1189 \r
1190 //---------------------------------------------------------------------------------------------------------\r
1191 \r
1192 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1193 #define XBOARD FALSE\r
1194 \r
1195 #define OPTCHAR "/"\r
1196 #define SEPCHAR "="\r
1197 #define TOPLEVEL 0\r
1198 \r
1199 #include "args.h"\r
1200 \r
1201 // front-end part of option handling\r
1202 \r
1203 VOID\r
1204 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1205 {\r
1206   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1207   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1208   DeleteDC(hdc);\r
1209   lf->lfWidth = 0;\r
1210   lf->lfEscapement = 0;\r
1211   lf->lfOrientation = 0;\r
1212   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1213   lf->lfItalic = mfp->italic;\r
1214   lf->lfUnderline = mfp->underline;\r
1215   lf->lfStrikeOut = mfp->strikeout;\r
1216   lf->lfCharSet = mfp->charset;\r
1217   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1218   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1219   lf->lfQuality = DEFAULT_QUALITY;\r
1220   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1221     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1222 }\r
1223 \r
1224 void\r
1225 CreateFontInMF(MyFont *mf)\r
1226\r
1227   LFfromMFP(&mf->lf, &mf->mfp);\r
1228   if (mf->hf) DeleteObject(mf->hf);\r
1229   mf->hf = CreateFontIndirect(&mf->lf);\r
1230 }\r
1231 \r
1232 // [HGM] This platform-dependent table provides the location for storing the color info\r
1233 void *\r
1234 colorVariable[] = {\r
1235   &whitePieceColor, \r
1236   &blackPieceColor, \r
1237   &lightSquareColor,\r
1238   &darkSquareColor, \r
1239   &highlightSquareColor,\r
1240   &premoveHighlightColor,\r
1241   NULL,\r
1242   &consoleBackgroundColor,\r
1243   &appData.fontForeColorWhite,\r
1244   &appData.fontBackColorWhite,\r
1245   &appData.fontForeColorBlack,\r
1246   &appData.fontBackColorBlack,\r
1247   &appData.evalHistColorWhite,\r
1248   &appData.evalHistColorBlack,\r
1249   &appData.highlightArrowColor,\r
1250 };\r
1251 \r
1252 /* Command line font name parser.  NULL name means do nothing.\r
1253    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1254    For backward compatibility, syntax without the colon is also\r
1255    accepted, but font names with digits in them won't work in that case.\r
1256 */\r
1257 VOID\r
1258 ParseFontName(char *name, MyFontParams *mfp)\r
1259 {\r
1260   char *p, *q;\r
1261   if (name == NULL) return;\r
1262   p = name;\r
1263   q = strchr(p, ':');\r
1264   if (q) {\r
1265     if (q - p >= sizeof(mfp->faceName))\r
1266       ExitArgError(_("Font name too long:"), name, TRUE);\r
1267     memcpy(mfp->faceName, p, q - p);\r
1268     mfp->faceName[q - p] = NULLCHAR;\r
1269     p = q + 1;\r
1270   } else {\r
1271     q = mfp->faceName;\r
1272     while (*p && !isdigit(*p)) {\r
1273       *q++ = *p++;\r
1274       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1275         ExitArgError(_("Font name too long:"), name, TRUE);\r
1276     }\r
1277     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1278     *q = NULLCHAR;\r
1279   }\r
1280   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1281   mfp->pointSize = (float) atof(p);\r
1282   mfp->bold = (strchr(p, 'b') != NULL);\r
1283   mfp->italic = (strchr(p, 'i') != NULL);\r
1284   mfp->underline = (strchr(p, 'u') != NULL);\r
1285   mfp->strikeout = (strchr(p, 's') != NULL);\r
1286   mfp->charset = DEFAULT_CHARSET;\r
1287   q = strchr(p, 'c');\r
1288   if (q)\r
1289     mfp->charset = (BYTE) atoi(q+1);\r
1290 }\r
1291 \r
1292 void\r
1293 ParseFont(char *name, int number)\r
1294 { // wrapper to shield back-end from 'font'\r
1295   ParseFontName(name, &font[boardSize][number]->mfp);\r
1296 }\r
1297 \r
1298 void\r
1299 SetFontDefaults()\r
1300 { // in WB  we have a 2D array of fonts; this initializes their description\r
1301   int i, j;\r
1302   /* Point font array elements to structures and\r
1303      parse default font names */\r
1304   for (i=0; i<NUM_FONTS; i++) {\r
1305     for (j=0; j<NUM_SIZES; j++) {\r
1306       font[j][i] = &fontRec[j][i];\r
1307       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1308     }\r
1309   }\r
1310 }\r
1311 \r
1312 void\r
1313 CreateFonts()\r
1314 { // here we create the actual fonts from the selected descriptions\r
1315   int i, j;\r
1316   for (i=0; i<NUM_FONTS; i++) {\r
1317     for (j=0; j<NUM_SIZES; j++) {\r
1318       CreateFontInMF(font[j][i]);\r
1319     }\r
1320   }\r
1321 }\r
1322 /* Color name parser.\r
1323    X version accepts X color names, but this one\r
1324    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1325 COLORREF\r
1326 ParseColorName(char *name)\r
1327 {\r
1328   int red, green, blue, count;\r
1329   char buf[MSG_SIZ];\r
1330 \r
1331   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1332   if (count != 3) {\r
1333     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1334       &red, &green, &blue);\r
1335   }\r
1336   if (count != 3) {\r
1337     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1338     DisplayError(buf, 0);\r
1339     return RGB(0, 0, 0);\r
1340   }\r
1341   return PALETTERGB(red, green, blue);\r
1342 }\r
1343 \r
1344 void\r
1345 ParseColor(int n, char *name)\r
1346 { // for WinBoard the color is an int, which needs to be derived from the string\r
1347   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1348 }\r
1349 \r
1350 void\r
1351 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1352 {\r
1353   char *e = argValue;\r
1354   int eff = 0;\r
1355 \r
1356   while (*e) {\r
1357     if (*e == 'b')      eff |= CFE_BOLD;\r
1358     else if (*e == 'i') eff |= CFE_ITALIC;\r
1359     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1360     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1361     else if (*e == '#' || isdigit(*e)) break;\r
1362     e++;\r
1363   }\r
1364   *effects = eff;\r
1365   *color   = ParseColorName(e);\r
1366 }\r
1367 \r
1368 void\r
1369 ParseTextAttribs(ColorClass cc, char *s)\r
1370 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1371     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1372     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1373 }\r
1374 \r
1375 void\r
1376 ParseBoardSize(void *addr, char *name)\r
1377 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1378   BoardSize bs = SizeTiny;\r
1379   while (sizeInfo[bs].name != NULL) {\r
1380     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1381         *(BoardSize *)addr = bs;\r
1382         return;\r
1383     }\r
1384     bs++;\r
1385   }\r
1386   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1387 }\r
1388 \r
1389 void\r
1390 LoadAllSounds()\r
1391 { // [HGM] import name from appData first\r
1392   ColorClass cc;\r
1393   SoundClass sc;\r
1394   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1395     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1396     textAttribs[cc].sound.data = NULL;\r
1397     MyLoadSound(&textAttribs[cc].sound);\r
1398   }\r
1399   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1400     textAttribs[cc].sound.name = strdup("");\r
1401     textAttribs[cc].sound.data = NULL;\r
1402   }\r
1403   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1404     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1405     sounds[sc].data = NULL;\r
1406     MyLoadSound(&sounds[sc]);\r
1407   }\r
1408 }\r
1409 \r
1410 void\r
1411 SetCommPortDefaults()\r
1412 {\r
1413    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1414   dcb.DCBlength = sizeof(DCB);\r
1415   dcb.BaudRate = 9600;\r
1416   dcb.fBinary = TRUE;\r
1417   dcb.fParity = FALSE;\r
1418   dcb.fOutxCtsFlow = FALSE;\r
1419   dcb.fOutxDsrFlow = FALSE;\r
1420   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1421   dcb.fDsrSensitivity = FALSE;\r
1422   dcb.fTXContinueOnXoff = TRUE;\r
1423   dcb.fOutX = FALSE;\r
1424   dcb.fInX = FALSE;\r
1425   dcb.fNull = FALSE;\r
1426   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1427   dcb.fAbortOnError = FALSE;\r
1428   dcb.ByteSize = 7;\r
1429   dcb.Parity = SPACEPARITY;\r
1430   dcb.StopBits = ONESTOPBIT;\r
1431 }\r
1432 \r
1433 // [HGM] args: these three cases taken out to stay in front-end\r
1434 void\r
1435 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1436 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1437         // while the curent board size determines the element. This system should be ported to XBoard.\r
1438         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1439         int bs;\r
1440         for (bs=0; bs<NUM_SIZES; bs++) {\r
1441           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1442           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1443           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1444             ad->argName, mfp->faceName, mfp->pointSize,\r
1445             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1446             mfp->bold ? "b" : "",\r
1447             mfp->italic ? "i" : "",\r
1448             mfp->underline ? "u" : "",\r
1449             mfp->strikeout ? "s" : "",\r
1450             (int)mfp->charset);\r
1451         }\r
1452       }\r
1453 \r
1454 void\r
1455 ExportSounds()\r
1456 { // [HGM] copy the names from the internal WB variables to appData\r
1457   ColorClass cc;\r
1458   SoundClass sc;\r
1459   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1460     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1461   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1462     (&appData.soundMove)[sc] = sounds[sc].name;\r
1463 }\r
1464 \r
1465 void\r
1466 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1467 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1468         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1469         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1470           (ta->effects & CFE_BOLD) ? "b" : "",\r
1471           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1472           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1473           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1474           (ta->effects) ? " " : "",\r
1475           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1476       }\r
1477 \r
1478 void\r
1479 SaveColor(FILE *f, ArgDescriptor *ad)\r
1480 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1481         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1482         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1483           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1484 }\r
1485 \r
1486 void\r
1487 SaveBoardSize(FILE *f, char *name, void *addr)\r
1488 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1489   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1490 }\r
1491 \r
1492 void\r
1493 ParseCommPortSettings(char *s)\r
1494 { // wrapper to keep dcb from back-end\r
1495   ParseCommSettings(s, &dcb);\r
1496 }\r
1497 \r
1498 void\r
1499 GetWindowCoords()\r
1500 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1501   GetActualPlacement(hwndMain, &wpMain);\r
1502   GetActualPlacement(hwndConsole, &wpConsole);\r
1503   GetActualPlacement(commentDialog, &wpComment);\r
1504   GetActualPlacement(editTagsDialog, &wpTags);\r
1505   GetActualPlacement(gameListDialog, &wpGameList);\r
1506   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1507   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1508   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1509 }\r
1510 \r
1511 void\r
1512 PrintCommPortSettings(FILE *f, char *name)\r
1513 { // wrapper to shield back-end from DCB\r
1514       PrintCommSettings(f, name, &dcb);\r
1515 }\r
1516 \r
1517 int\r
1518 MySearchPath(char *installDir, char *name, char *fullname)\r
1519 {\r
1520   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1521   if(name[0]== '%') {\r
1522     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1523     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1524       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1525       *strchr(buf, '%') = 0;\r
1526       strcat(fullname, getenv(buf));\r
1527       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1528     }\r
1529     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1530     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1531     return (int) strlen(fullname);\r
1532   }\r
1533   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1534 }\r
1535 \r
1536 int\r
1537 MyGetFullPathName(char *name, char *fullname)\r
1538 {\r
1539   char *dummy;\r
1540   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1541 }\r
1542 \r
1543 int\r
1544 MainWindowUp()\r
1545 { // [HGM] args: allows testing if main window is realized from back-end\r
1546   return hwndMain != NULL;\r
1547 }\r
1548 \r
1549 void\r
1550 PopUpStartupDialog()\r
1551 {\r
1552     FARPROC lpProc;\r
1553     \r
1554     LoadLanguageFile(appData.language);\r
1555     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1556     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1557     FreeProcInstance(lpProc);\r
1558 }\r
1559 \r
1560 /*---------------------------------------------------------------------------*\\r
1561  *\r
1562  * GDI board drawing routines\r
1563  *\r
1564 \*---------------------------------------------------------------------------*/\r
1565 \r
1566 /* [AS] Draw square using background texture */\r
1567 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1568 {\r
1569     XFORM   x;\r
1570 \r
1571     if( mode == 0 ) {\r
1572         return; /* Should never happen! */\r
1573     }\r
1574 \r
1575     SetGraphicsMode( dst, GM_ADVANCED );\r
1576 \r
1577     switch( mode ) {\r
1578     case 1:\r
1579         /* Identity */\r
1580         break;\r
1581     case 2:\r
1582         /* X reflection */\r
1583         x.eM11 = -1.0;\r
1584         x.eM12 = 0;\r
1585         x.eM21 = 0;\r
1586         x.eM22 = 1.0;\r
1587         x.eDx = (FLOAT) dw + dx - 1;\r
1588         x.eDy = 0;\r
1589         dx = 0;\r
1590         SetWorldTransform( dst, &x );\r
1591         break;\r
1592     case 3:\r
1593         /* Y reflection */\r
1594         x.eM11 = 1.0;\r
1595         x.eM12 = 0;\r
1596         x.eM21 = 0;\r
1597         x.eM22 = -1.0;\r
1598         x.eDx = 0;\r
1599         x.eDy = (FLOAT) dh + dy - 1;\r
1600         dy = 0;\r
1601         SetWorldTransform( dst, &x );\r
1602         break;\r
1603     case 4:\r
1604         /* X/Y flip */\r
1605         x.eM11 = 0;\r
1606         x.eM12 = 1.0;\r
1607         x.eM21 = 1.0;\r
1608         x.eM22 = 0;\r
1609         x.eDx = (FLOAT) dx;\r
1610         x.eDy = (FLOAT) dy;\r
1611         dx = 0;\r
1612         dy = 0;\r
1613         SetWorldTransform( dst, &x );\r
1614         break;\r
1615     }\r
1616 \r
1617     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1618 \r
1619     x.eM11 = 1.0;\r
1620     x.eM12 = 0;\r
1621     x.eM21 = 0;\r
1622     x.eM22 = 1.0;\r
1623     x.eDx = 0;\r
1624     x.eDy = 0;\r
1625     SetWorldTransform( dst, &x );\r
1626 \r
1627     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1628 }\r
1629 \r
1630 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1631 enum {\r
1632     PM_WP = (int) WhitePawn, \r
1633     PM_WN = (int) WhiteKnight, \r
1634     PM_WB = (int) WhiteBishop, \r
1635     PM_WR = (int) WhiteRook, \r
1636     PM_WQ = (int) WhiteQueen, \r
1637     PM_WF = (int) WhiteFerz, \r
1638     PM_WW = (int) WhiteWazir, \r
1639     PM_WE = (int) WhiteAlfil, \r
1640     PM_WM = (int) WhiteMan, \r
1641     PM_WO = (int) WhiteCannon, \r
1642     PM_WU = (int) WhiteUnicorn, \r
1643     PM_WH = (int) WhiteNightrider, \r
1644     PM_WA = (int) WhiteAngel, \r
1645     PM_WC = (int) WhiteMarshall, \r
1646     PM_WAB = (int) WhiteCardinal, \r
1647     PM_WD = (int) WhiteDragon, \r
1648     PM_WL = (int) WhiteLance, \r
1649     PM_WS = (int) WhiteCobra, \r
1650     PM_WV = (int) WhiteFalcon, \r
1651     PM_WSG = (int) WhiteSilver, \r
1652     PM_WG = (int) WhiteGrasshopper, \r
1653     PM_WK = (int) WhiteKing,\r
1654     PM_BP = (int) BlackPawn, \r
1655     PM_BN = (int) BlackKnight, \r
1656     PM_BB = (int) BlackBishop, \r
1657     PM_BR = (int) BlackRook, \r
1658     PM_BQ = (int) BlackQueen, \r
1659     PM_BF = (int) BlackFerz, \r
1660     PM_BW = (int) BlackWazir, \r
1661     PM_BE = (int) BlackAlfil, \r
1662     PM_BM = (int) BlackMan,\r
1663     PM_BO = (int) BlackCannon, \r
1664     PM_BU = (int) BlackUnicorn, \r
1665     PM_BH = (int) BlackNightrider, \r
1666     PM_BA = (int) BlackAngel, \r
1667     PM_BC = (int) BlackMarshall, \r
1668     PM_BG = (int) BlackGrasshopper, \r
1669     PM_BAB = (int) BlackCardinal,\r
1670     PM_BD = (int) BlackDragon,\r
1671     PM_BL = (int) BlackLance,\r
1672     PM_BS = (int) BlackCobra,\r
1673     PM_BV = (int) BlackFalcon,\r
1674     PM_BSG = (int) BlackSilver,\r
1675     PM_BK = (int) BlackKing\r
1676 };\r
1677 \r
1678 static HFONT hPieceFont = NULL;\r
1679 static HBITMAP hPieceMask[(int) EmptySquare];\r
1680 static HBITMAP hPieceFace[(int) EmptySquare];\r
1681 static int fontBitmapSquareSize = 0;\r
1682 static char pieceToFontChar[(int) EmptySquare] =\r
1683                               { 'p', 'n', 'b', 'r', 'q', \r
1684                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1685                       'k', 'o', 'm', 'v', 't', 'w', \r
1686                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1687                                                               'l' };\r
1688 \r
1689 extern BOOL SetCharTable( char *table, const char * map );\r
1690 /* [HGM] moved to backend.c */\r
1691 \r
1692 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1693 {\r
1694     HBRUSH hbrush;\r
1695     BYTE r1 = GetRValue( color );\r
1696     BYTE g1 = GetGValue( color );\r
1697     BYTE b1 = GetBValue( color );\r
1698     BYTE r2 = r1 / 2;\r
1699     BYTE g2 = g1 / 2;\r
1700     BYTE b2 = b1 / 2;\r
1701     RECT rc;\r
1702 \r
1703     /* Create a uniform background first */\r
1704     hbrush = CreateSolidBrush( color );\r
1705     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1706     FillRect( hdc, &rc, hbrush );\r
1707     DeleteObject( hbrush );\r
1708     \r
1709     if( mode == 1 ) {\r
1710         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1711         int steps = squareSize / 2;\r
1712         int i;\r
1713 \r
1714         for( i=0; i<steps; i++ ) {\r
1715             BYTE r = r1 - (r1-r2) * i / steps;\r
1716             BYTE g = g1 - (g1-g2) * i / steps;\r
1717             BYTE b = b1 - (b1-b2) * i / steps;\r
1718 \r
1719             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1720             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1721             FillRect( hdc, &rc, hbrush );\r
1722             DeleteObject(hbrush);\r
1723         }\r
1724     }\r
1725     else if( mode == 2 ) {\r
1726         /* Diagonal gradient, good more or less for every piece */\r
1727         POINT triangle[3];\r
1728         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1729         HBRUSH hbrush_old;\r
1730         int steps = squareSize;\r
1731         int i;\r
1732 \r
1733         triangle[0].x = squareSize - steps;\r
1734         triangle[0].y = squareSize;\r
1735         triangle[1].x = squareSize;\r
1736         triangle[1].y = squareSize;\r
1737         triangle[2].x = squareSize;\r
1738         triangle[2].y = squareSize - steps;\r
1739 \r
1740         for( i=0; i<steps; i++ ) {\r
1741             BYTE r = r1 - (r1-r2) * i / steps;\r
1742             BYTE g = g1 - (g1-g2) * i / steps;\r
1743             BYTE b = b1 - (b1-b2) * i / steps;\r
1744 \r
1745             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1746             hbrush_old = SelectObject( hdc, hbrush );\r
1747             Polygon( hdc, triangle, 3 );\r
1748             SelectObject( hdc, hbrush_old );\r
1749             DeleteObject(hbrush);\r
1750             triangle[0].x++;\r
1751             triangle[2].y++;\r
1752         }\r
1753 \r
1754         SelectObject( hdc, hpen );\r
1755     }\r
1756 }\r
1757 \r
1758 /*\r
1759     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1760     seems to work ok. The main problem here is to find the "inside" of a chess\r
1761     piece: follow the steps as explained below.\r
1762 */\r
1763 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1764 {\r
1765     HBITMAP hbm;\r
1766     HBITMAP hbm_old;\r
1767     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1768     RECT rc;\r
1769     SIZE sz;\r
1770     POINT pt;\r
1771     int backColor = whitePieceColor; \r
1772     int foreColor = blackPieceColor;\r
1773     \r
1774     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1775         backColor = appData.fontBackColorWhite;\r
1776         foreColor = appData.fontForeColorWhite;\r
1777     }\r
1778     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1779         backColor = appData.fontBackColorBlack;\r
1780         foreColor = appData.fontForeColorBlack;\r
1781     }\r
1782 \r
1783     /* Mask */\r
1784     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1785 \r
1786     hbm_old = SelectObject( hdc, hbm );\r
1787 \r
1788     rc.left = 0;\r
1789     rc.top = 0;\r
1790     rc.right = squareSize;\r
1791     rc.bottom = squareSize;\r
1792 \r
1793     /* Step 1: background is now black */\r
1794     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1795 \r
1796     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1797 \r
1798     pt.x = (squareSize - sz.cx) / 2;\r
1799     pt.y = (squareSize - sz.cy) / 2;\r
1800 \r
1801     SetBkMode( hdc, TRANSPARENT );\r
1802     SetTextColor( hdc, chroma );\r
1803     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1804     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1805 \r
1806     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1807     /* Step 3: the area outside the piece is filled with white */\r
1808 //    FloodFill( hdc, 0, 0, chroma );\r
1809     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1810     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1811     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1812     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1813     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1814     /* \r
1815         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1816         but if the start point is not inside the piece we're lost!\r
1817         There should be a better way to do this... if we could create a region or path\r
1818         from the fill operation we would be fine for example.\r
1819     */\r
1820 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1821     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1822 \r
1823     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1824         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1825         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1826 \r
1827         SelectObject( dc2, bm2 );\r
1828         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1829         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1830         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1831         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1832         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1833 \r
1834         DeleteDC( dc2 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, 0 );\r
1839     /* \r
1840         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1841         draw the piece again in black for safety.\r
1842     */\r
1843     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1844 \r
1845     SelectObject( hdc, hbm_old );\r
1846 \r
1847     if( hPieceMask[index] != NULL ) {\r
1848         DeleteObject( hPieceMask[index] );\r
1849     }\r
1850 \r
1851     hPieceMask[index] = hbm;\r
1852 \r
1853     /* Face */\r
1854     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1855 \r
1856     SelectObject( hdc, hbm );\r
1857 \r
1858     {\r
1859         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1860         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1861         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1862 \r
1863         SelectObject( dc1, hPieceMask[index] );\r
1864         SelectObject( dc2, bm2 );\r
1865         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1866         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1867         \r
1868         /* \r
1869             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1870             the piece background and deletes (makes transparent) the rest.\r
1871             Thanks to that mask, we are free to paint the background with the greates\r
1872             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1873             We use this, to make gradients and give the pieces a "roundish" look.\r
1874         */\r
1875         SetPieceBackground( hdc, backColor, 2 );\r
1876         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1877 \r
1878         DeleteDC( dc2 );\r
1879         DeleteDC( dc1 );\r
1880         DeleteObject( bm2 );\r
1881     }\r
1882 \r
1883     SetTextColor( hdc, foreColor );\r
1884     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1885 \r
1886     SelectObject( hdc, hbm_old );\r
1887 \r
1888     if( hPieceFace[index] != NULL ) {\r
1889         DeleteObject( hPieceFace[index] );\r
1890     }\r
1891 \r
1892     hPieceFace[index] = hbm;\r
1893 }\r
1894 \r
1895 static int TranslatePieceToFontPiece( int piece )\r
1896 {\r
1897     switch( piece ) {\r
1898     case BlackPawn:\r
1899         return PM_BP;\r
1900     case BlackKnight:\r
1901         return PM_BN;\r
1902     case BlackBishop:\r
1903         return PM_BB;\r
1904     case BlackRook:\r
1905         return PM_BR;\r
1906     case BlackQueen:\r
1907         return PM_BQ;\r
1908     case BlackKing:\r
1909         return PM_BK;\r
1910     case WhitePawn:\r
1911         return PM_WP;\r
1912     case WhiteKnight:\r
1913         return PM_WN;\r
1914     case WhiteBishop:\r
1915         return PM_WB;\r
1916     case WhiteRook:\r
1917         return PM_WR;\r
1918     case WhiteQueen:\r
1919         return PM_WQ;\r
1920     case WhiteKing:\r
1921         return PM_WK;\r
1922 \r
1923     case BlackAngel:\r
1924         return PM_BA;\r
1925     case BlackMarshall:\r
1926         return PM_BC;\r
1927     case BlackFerz:\r
1928         return PM_BF;\r
1929     case BlackNightrider:\r
1930         return PM_BH;\r
1931     case BlackAlfil:\r
1932         return PM_BE;\r
1933     case BlackWazir:\r
1934         return PM_BW;\r
1935     case BlackUnicorn:\r
1936         return PM_BU;\r
1937     case BlackCannon:\r
1938         return PM_BO;\r
1939     case BlackGrasshopper:\r
1940         return PM_BG;\r
1941     case BlackMan:\r
1942         return PM_BM;\r
1943     case BlackSilver:\r
1944         return PM_BSG;\r
1945     case BlackLance:\r
1946         return PM_BL;\r
1947     case BlackFalcon:\r
1948         return PM_BV;\r
1949     case BlackCobra:\r
1950         return PM_BS;\r
1951     case BlackCardinal:\r
1952         return PM_BAB;\r
1953     case BlackDragon:\r
1954         return PM_BD;\r
1955 \r
1956     case WhiteAngel:\r
1957         return PM_WA;\r
1958     case WhiteMarshall:\r
1959         return PM_WC;\r
1960     case WhiteFerz:\r
1961         return PM_WF;\r
1962     case WhiteNightrider:\r
1963         return PM_WH;\r
1964     case WhiteAlfil:\r
1965         return PM_WE;\r
1966     case WhiteWazir:\r
1967         return PM_WW;\r
1968     case WhiteUnicorn:\r
1969         return PM_WU;\r
1970     case WhiteCannon:\r
1971         return PM_WO;\r
1972     case WhiteGrasshopper:\r
1973         return PM_WG;\r
1974     case WhiteMan:\r
1975         return PM_WM;\r
1976     case WhiteSilver:\r
1977         return PM_WSG;\r
1978     case WhiteLance:\r
1979         return PM_WL;\r
1980     case WhiteFalcon:\r
1981         return PM_WV;\r
1982     case WhiteCobra:\r
1983         return PM_WS;\r
1984     case WhiteCardinal:\r
1985         return PM_WAB;\r
1986     case WhiteDragon:\r
1987         return PM_WD;\r
1988     }\r
1989 \r
1990     return 0;\r
1991 }\r
1992 \r
1993 void CreatePiecesFromFont()\r
1994 {\r
1995     LOGFONT lf;\r
1996     HDC hdc_window = NULL;\r
1997     HDC hdc = NULL;\r
1998     HFONT hfont_old;\r
1999     int fontHeight;\r
2000     int i;\r
2001 \r
2002     if( fontBitmapSquareSize < 0 ) {\r
2003         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2004         return;\r
2005     }\r
2006 \r
2007     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2008             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2009         fontBitmapSquareSize = -1;\r
2010         return;\r
2011     }\r
2012 \r
2013     if( fontBitmapSquareSize != squareSize ) {\r
2014         hdc_window = GetDC( hwndMain );\r
2015         hdc = CreateCompatibleDC( hdc_window );\r
2016 \r
2017         if( hPieceFont != NULL ) {\r
2018             DeleteObject( hPieceFont );\r
2019         }\r
2020         else {\r
2021             for( i=0; i<=(int)BlackKing; i++ ) {\r
2022                 hPieceMask[i] = NULL;\r
2023                 hPieceFace[i] = NULL;\r
2024             }\r
2025         }\r
2026 \r
2027         fontHeight = 75;\r
2028 \r
2029         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2030             fontHeight = appData.fontPieceSize;\r
2031         }\r
2032 \r
2033         fontHeight = (fontHeight * squareSize) / 100;\r
2034 \r
2035         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2036         lf.lfWidth = 0;\r
2037         lf.lfEscapement = 0;\r
2038         lf.lfOrientation = 0;\r
2039         lf.lfWeight = FW_NORMAL;\r
2040         lf.lfItalic = 0;\r
2041         lf.lfUnderline = 0;\r
2042         lf.lfStrikeOut = 0;\r
2043         lf.lfCharSet = DEFAULT_CHARSET;\r
2044         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2045         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2046         lf.lfQuality = PROOF_QUALITY;\r
2047         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2048         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2049         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2050 \r
2051         hPieceFont = CreateFontIndirect( &lf );\r
2052 \r
2053         if( hPieceFont == NULL ) {\r
2054             fontBitmapSquareSize = -2;\r
2055         }\r
2056         else {\r
2057             /* Setup font-to-piece character table */\r
2058             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2059                 /* No (or wrong) global settings, try to detect the font */\r
2060                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2061                     /* Alpha */\r
2062                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2063                 }\r
2064                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2065                     /* DiagramTT* family */\r
2066                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2067                 }\r
2068                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2069                     /* Fairy symbols */\r
2070                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2071                 }\r
2072                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2073                     /* Good Companion (Some characters get warped as literal :-( */\r
2074                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2075                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2076                     SetCharTable(pieceToFontChar, s);\r
2077                 }\r
2078                 else {\r
2079                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2080                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2081                 }\r
2082             }\r
2083 \r
2084             /* Create bitmaps */\r
2085             hfont_old = SelectObject( hdc, hPieceFont );\r
2086             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2087                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2088                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2089 \r
2090             SelectObject( hdc, hfont_old );\r
2091 \r
2092             fontBitmapSquareSize = squareSize;\r
2093         }\r
2094     }\r
2095 \r
2096     if( hdc != NULL ) {\r
2097         DeleteDC( hdc );\r
2098     }\r
2099 \r
2100     if( hdc_window != NULL ) {\r
2101         ReleaseDC( hwndMain, hdc_window );\r
2102     }\r
2103 }\r
2104 \r
2105 HBITMAP\r
2106 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2107 {\r
2108   char name[128];\r
2109 \r
2110     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2111   if (gameInfo.event &&\r
2112       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2113       strcmp(name, "k80s") == 0) {\r
2114     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2115   }\r
2116   return LoadBitmap(hinst, name);\r
2117 }\r
2118 \r
2119 \r
2120 /* Insert a color into the program's logical palette\r
2121    structure.  This code assumes the given color is\r
2122    the result of the RGB or PALETTERGB macro, and it\r
2123    knows how those macros work (which is documented).\r
2124 */\r
2125 VOID\r
2126 InsertInPalette(COLORREF color)\r
2127 {\r
2128   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2129 \r
2130   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2131     DisplayFatalError(_("Too many colors"), 0, 1);\r
2132     pLogPal->palNumEntries--;\r
2133     return;\r
2134   }\r
2135 \r
2136   pe->peFlags = (char) 0;\r
2137   pe->peRed = (char) (0xFF & color);\r
2138   pe->peGreen = (char) (0xFF & (color >> 8));\r
2139   pe->peBlue = (char) (0xFF & (color >> 16));\r
2140   return;\r
2141 }\r
2142 \r
2143 \r
2144 VOID\r
2145 InitDrawingColors()\r
2146 {\r
2147   if (pLogPal == NULL) {\r
2148     /* Allocate enough memory for a logical palette with\r
2149      * PALETTESIZE entries and set the size and version fields\r
2150      * of the logical palette structure.\r
2151      */\r
2152     pLogPal = (NPLOGPALETTE)\r
2153       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2154                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2155     pLogPal->palVersion    = 0x300;\r
2156   }\r
2157   pLogPal->palNumEntries = 0;\r
2158 \r
2159   InsertInPalette(lightSquareColor);\r
2160   InsertInPalette(darkSquareColor);\r
2161   InsertInPalette(whitePieceColor);\r
2162   InsertInPalette(blackPieceColor);\r
2163   InsertInPalette(highlightSquareColor);\r
2164   InsertInPalette(premoveHighlightColor);\r
2165 \r
2166   /*  create a logical color palette according the information\r
2167    *  in the LOGPALETTE structure.\r
2168    */\r
2169   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2170 \r
2171   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2172   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2173   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2174   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2175   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2176   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2177   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2178   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2179   /* [AS] Force rendering of the font-based pieces */\r
2180   if( fontBitmapSquareSize > 0 ) {\r
2181     fontBitmapSquareSize = 0;\r
2182   }\r
2183 }\r
2184 \r
2185 \r
2186 int\r
2187 BoardWidth(int boardSize, int n)\r
2188 { /* [HGM] argument n added to allow different width and height */\r
2189   int lineGap = sizeInfo[boardSize].lineGap;\r
2190 \r
2191   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2192       lineGap = appData.overrideLineGap;\r
2193   }\r
2194 \r
2195   return (n + 1) * lineGap +\r
2196           n * sizeInfo[boardSize].squareSize;\r
2197 }\r
2198 \r
2199 /* Respond to board resize by dragging edge */\r
2200 VOID\r
2201 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2202 {\r
2203   BoardSize newSize = NUM_SIZES - 1;\r
2204   static int recurse = 0;\r
2205   if (IsIconic(hwndMain)) return;\r
2206   if (recurse > 0) return;\r
2207   recurse++;\r
2208   while (newSize > 0) {\r
2209         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2210         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2211            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2212     newSize--;\r
2213   } \r
2214   boardSize = newSize;\r
2215   InitDrawingSizes(boardSize, flags);\r
2216   recurse--;\r
2217 }\r
2218 \r
2219 \r
2220 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2221 \r
2222 VOID\r
2223 InitDrawingSizes(BoardSize boardSize, int flags)\r
2224 {\r
2225   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2226   ChessSquare piece;\r
2227   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2228   HDC hdc;\r
2229   SIZE clockSize, messageSize;\r
2230   HFONT oldFont;\r
2231   char buf[MSG_SIZ];\r
2232   char *str;\r
2233   HMENU hmenu = GetMenu(hwndMain);\r
2234   RECT crect, wrect, oldRect;\r
2235   int offby;\r
2236   LOGBRUSH logbrush;\r
2237 \r
2238   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2239   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2240 \r
2241   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2242   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2243 \r
2244   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2245   oldRect.top = wpMain.y;\r
2246   oldRect.right = wpMain.x + wpMain.width;\r
2247   oldRect.bottom = wpMain.y + wpMain.height;\r
2248 \r
2249   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2250   smallLayout = sizeInfo[boardSize].smallLayout;\r
2251   squareSize = sizeInfo[boardSize].squareSize;\r
2252   lineGap = sizeInfo[boardSize].lineGap;\r
2253   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2254 \r
2255   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2256       lineGap = appData.overrideLineGap;\r
2257   }\r
2258 \r
2259   if (tinyLayout != oldTinyLayout) {\r
2260     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2261     if (tinyLayout) {\r
2262       style &= ~WS_SYSMENU;\r
2263       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2264                  "&Minimize\tCtrl+F4");\r
2265     } else {\r
2266       style |= WS_SYSMENU;\r
2267       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2268     }\r
2269     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2270 \r
2271     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2272       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2273         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2274     }\r
2275     DrawMenuBar(hwndMain);\r
2276   }\r
2277 \r
2278   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2279   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2280 \r
2281   /* Get text area sizes */\r
2282   hdc = GetDC(hwndMain);\r
2283   if (appData.clockMode) {\r
2284     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2285   } else {\r
2286     snprintf(buf, MSG_SIZ, _("White"));\r
2287   }\r
2288   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2289   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2290   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2291   str = _("We only care about the height here");\r
2292   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2293   SelectObject(hdc, oldFont);\r
2294   ReleaseDC(hwndMain, hdc);\r
2295 \r
2296   /* Compute where everything goes */\r
2297   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2298         /* [HGM] logo: if either logo is on, reserve space for it */\r
2299         logoHeight =  2*clockSize.cy;\r
2300         leftLogoRect.left   = OUTER_MARGIN;\r
2301         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2302         leftLogoRect.top    = OUTER_MARGIN;\r
2303         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2304 \r
2305         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2306         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2307         rightLogoRect.top    = OUTER_MARGIN;\r
2308         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2309 \r
2310 \r
2311     whiteRect.left = leftLogoRect.right;\r
2312     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2313     whiteRect.top = OUTER_MARGIN;\r
2314     whiteRect.bottom = whiteRect.top + logoHeight;\r
2315 \r
2316     blackRect.right = rightLogoRect.left;\r
2317     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2318     blackRect.top = whiteRect.top;\r
2319     blackRect.bottom = whiteRect.bottom;\r
2320   } else {\r
2321     whiteRect.left = OUTER_MARGIN;\r
2322     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2323     whiteRect.top = OUTER_MARGIN;\r
2324     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2325 \r
2326     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2327     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2328     blackRect.top = whiteRect.top;\r
2329     blackRect.bottom = whiteRect.bottom;\r
2330 \r
2331     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2332   }\r
2333 \r
2334   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2335   if (appData.showButtonBar) {\r
2336     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2337       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2338   } else {\r
2339     messageRect.right = OUTER_MARGIN + boardWidth;\r
2340   }\r
2341   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2342   messageRect.bottom = messageRect.top + messageSize.cy;\r
2343 \r
2344   boardRect.left = OUTER_MARGIN;\r
2345   boardRect.right = boardRect.left + boardWidth;\r
2346   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2347   boardRect.bottom = boardRect.top + boardHeight;\r
2348 \r
2349   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2350   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2351   oldBoardSize = boardSize;\r
2352   oldTinyLayout = tinyLayout;\r
2353   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2354   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2355     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2356   winW *= 1 + twoBoards;\r
2357   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2358   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2359   wpMain.height = winH; //       without disturbing window attachments\r
2360   GetWindowRect(hwndMain, &wrect);\r
2361   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2362                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2363 \r
2364   // [HGM] placement: let attached windows follow size change.\r
2365   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2366   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2367   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2368   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2369   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2370 \r
2371   /* compensate if menu bar wrapped */\r
2372   GetClientRect(hwndMain, &crect);\r
2373   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2374   wpMain.height += offby;\r
2375   switch (flags) {\r
2376   case WMSZ_TOPLEFT:\r
2377     SetWindowPos(hwndMain, NULL, \r
2378                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2379                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2380     break;\r
2381 \r
2382   case WMSZ_TOPRIGHT:\r
2383   case WMSZ_TOP:\r
2384     SetWindowPos(hwndMain, NULL, \r
2385                  wrect.left, wrect.bottom - wpMain.height, \r
2386                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2387     break;\r
2388 \r
2389   case WMSZ_BOTTOMLEFT:\r
2390   case WMSZ_LEFT:\r
2391     SetWindowPos(hwndMain, NULL, \r
2392                  wrect.right - wpMain.width, wrect.top, \r
2393                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2394     break;\r
2395 \r
2396   case WMSZ_BOTTOMRIGHT:\r
2397   case WMSZ_BOTTOM:\r
2398   case WMSZ_RIGHT:\r
2399   default:\r
2400     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2401                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2402     break;\r
2403   }\r
2404 \r
2405   hwndPause = NULL;\r
2406   for (i = 0; i < N_BUTTONS; i++) {\r
2407     if (buttonDesc[i].hwnd != NULL) {\r
2408       DestroyWindow(buttonDesc[i].hwnd);\r
2409       buttonDesc[i].hwnd = NULL;\r
2410     }\r
2411     if (appData.showButtonBar) {\r
2412       buttonDesc[i].hwnd =\r
2413         CreateWindow("BUTTON", buttonDesc[i].label,\r
2414                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2415                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2416                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2417                      (HMENU) buttonDesc[i].id,\r
2418                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2419       if (tinyLayout) {\r
2420         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2421                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2422                     MAKELPARAM(FALSE, 0));\r
2423       }\r
2424       if (buttonDesc[i].id == IDM_Pause)\r
2425         hwndPause = buttonDesc[i].hwnd;\r
2426       buttonDesc[i].wndproc = (WNDPROC)\r
2427         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2428     }\r
2429   }\r
2430   if (gridPen != NULL) DeleteObject(gridPen);\r
2431   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2432   if (premovePen != NULL) DeleteObject(premovePen);\r
2433   if (lineGap != 0) {\r
2434     logbrush.lbStyle = BS_SOLID;\r
2435     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2436     gridPen =\r
2437       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2438                    lineGap, &logbrush, 0, NULL);\r
2439     logbrush.lbColor = highlightSquareColor;\r
2440     highlightPen =\r
2441       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2442                    lineGap, &logbrush, 0, NULL);\r
2443 \r
2444     logbrush.lbColor = premoveHighlightColor; \r
2445     premovePen =\r
2446       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2447                    lineGap, &logbrush, 0, NULL);\r
2448 \r
2449     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2450     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2451       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2452       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2453         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2454       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2455         BOARD_WIDTH * (squareSize + lineGap);\r
2456       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2457     }\r
2458     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2459       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2460       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2461         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2462         lineGap / 2 + (i * (squareSize + lineGap));\r
2463       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2464         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2465       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2466     }\r
2467   }\r
2468 \r
2469   /* [HGM] Licensing requirement */\r
2470 #ifdef GOTHIC\r
2471   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2472 #endif\r
2473 #ifdef FALCON\r
2474   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2475 #endif\r
2476   GothicPopUp( "", VariantNormal);\r
2477 \r
2478 \r
2479 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2480 \r
2481   /* Load piece bitmaps for this board size */\r
2482   for (i=0; i<=2; i++) {\r
2483     for (piece = WhitePawn;\r
2484          (int) piece < (int) BlackPawn;\r
2485          piece = (ChessSquare) ((int) piece + 1)) {\r
2486       if (pieceBitmap[i][piece] != NULL)\r
2487         DeleteObject(pieceBitmap[i][piece]);\r
2488     }\r
2489   }\r
2490 \r
2491   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2492   // Orthodox Chess pieces\r
2493   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2494   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2495   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2496   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2497   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2498   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2499   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2500   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2501   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2502   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2503   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2504   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2505   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2506   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2507   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2508   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2509     // in Shogi, Hijack the unused Queen for Lance\r
2510     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2511     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2512     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2513   } else {\r
2514     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2515     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2516     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2517   }\r
2518 \r
2519   if(squareSize <= 72 && squareSize >= 33) { \r
2520     /* A & C are available in most sizes now */\r
2521     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2522       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2523       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2524       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2525       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2526       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2527       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2528       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2529       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2530       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2531       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2532       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2533       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2534     } else { // Smirf-like\r
2535       if(gameInfo.variant == VariantSChess) {\r
2536         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2537         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2538         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2539       } else {\r
2540         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2541         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2542         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2543       }\r
2544     }\r
2545     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2546       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2547       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2548       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2549     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2550       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2551       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2552       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2553     } else { // WinBoard standard\r
2554       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2555       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2556       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2557     }\r
2558   }\r
2559 \r
2560 \r
2561   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2562     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2563     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2564     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2565     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2566     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2567     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2568     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2569     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2570     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2571     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2572     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2573     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2574     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2575     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2576     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2577     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2578     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2579     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2580     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2581     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2582     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2583     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2584     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2585     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2586     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2587     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2588     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2589     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2590     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2591     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2592 \r
2593     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2594       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2595       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2596       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2597       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2598       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2599       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2600       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2601       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2602       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2603       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2604       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2605       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2606     } else {\r
2607       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2608       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2609       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2610       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2611       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2612       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2613       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2614       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2615       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2616       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2617       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2618       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2619     }\r
2620 \r
2621   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2622     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2623     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2624     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2625     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2626     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2627     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2628     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2629     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2630     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2631     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2632     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2633     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2634     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2635     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2636   }\r
2637 \r
2638 \r
2639   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2640   /* special Shogi support in this size */\r
2641   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2642       for (piece = WhitePawn;\r
2643            (int) piece < (int) BlackPawn;\r
2644            piece = (ChessSquare) ((int) piece + 1)) {\r
2645         if (pieceBitmap[i][piece] != NULL)\r
2646           DeleteObject(pieceBitmap[i][piece]);\r
2647       }\r
2648     }\r
2649   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2650   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2651   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2652   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2653   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2654   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2655   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2656   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2657   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2658   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2659   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2660   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2661   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2662   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2663   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2664   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2665   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2666   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2667   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2668   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2669   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2670   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2671   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2672   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2673   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2674   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2675   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2676   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2677   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2678   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2679   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2680   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2681   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2682   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2683   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2684   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2685   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2686   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2687   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2688   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2689   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2690   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2691   minorSize = 0;\r
2692   }\r
2693 }\r
2694 \r
2695 HBITMAP\r
2696 PieceBitmap(ChessSquare p, int kind)\r
2697 {\r
2698   if ((int) p >= (int) BlackPawn)\r
2699     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2700 \r
2701   return pieceBitmap[kind][(int) p];\r
2702 }\r
2703 \r
2704 /***************************************************************/\r
2705 \r
2706 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2707 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2708 /*\r
2709 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2710 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2711 */\r
2712 \r
2713 VOID\r
2714 SquareToPos(int row, int column, int * x, int * y)\r
2715 {\r
2716   if (flipView) {\r
2717     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2718     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2719   } else {\r
2720     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2721     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2722   }\r
2723 }\r
2724 \r
2725 VOID\r
2726 DrawCoordsOnDC(HDC hdc)\r
2727 {\r
2728   static char files[] = "0123456789012345678901221098765432109876543210";\r
2729   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2730   char str[2] = { NULLCHAR, NULLCHAR };\r
2731   int oldMode, oldAlign, x, y, start, i;\r
2732   HFONT oldFont;\r
2733   HBRUSH oldBrush;\r
2734 \r
2735   if (!appData.showCoords)\r
2736     return;\r
2737 \r
2738   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2739 \r
2740   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2741   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2742   oldAlign = GetTextAlign(hdc);\r
2743   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2744 \r
2745   y = boardRect.top + lineGap;\r
2746   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2747 \r
2748   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2749   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2750     str[0] = files[start + i];\r
2751     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2752     y += squareSize + lineGap;\r
2753   }\r
2754 \r
2755   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2756 \r
2757   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2758   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2759     str[0] = ranks[start + i];\r
2760     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2761     x += squareSize + lineGap;\r
2762   }    \r
2763 \r
2764   SelectObject(hdc, oldBrush);\r
2765   SetBkMode(hdc, oldMode);\r
2766   SetTextAlign(hdc, oldAlign);\r
2767   SelectObject(hdc, oldFont);\r
2768 }\r
2769 \r
2770 VOID\r
2771 DrawGridOnDC(HDC hdc)\r
2772 {\r
2773   HPEN oldPen;\r
2774  \r
2775   if (lineGap != 0) {\r
2776     oldPen = SelectObject(hdc, gridPen);\r
2777     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2778     SelectObject(hdc, oldPen);\r
2779   }\r
2780 }\r
2781 \r
2782 #define HIGHLIGHT_PEN 0\r
2783 #define PREMOVE_PEN   1\r
2784 \r
2785 VOID\r
2786 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2787 {\r
2788   int x1, y1;\r
2789   HPEN oldPen, hPen;\r
2790   if (lineGap == 0) return;\r
2791   if (flipView) {\r
2792     x1 = boardRect.left +\r
2793       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2794     y1 = boardRect.top +\r
2795       lineGap/2 + y * (squareSize + lineGap);\r
2796   } else {\r
2797     x1 = boardRect.left +\r
2798       lineGap/2 + x * (squareSize + lineGap);\r
2799     y1 = boardRect.top +\r
2800       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2801   }\r
2802   hPen = pen ? premovePen : highlightPen;\r
2803   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2804   MoveToEx(hdc, x1, y1, NULL);\r
2805   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2806   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2807   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2808   LineTo(hdc, x1, y1);\r
2809   SelectObject(hdc, oldPen);\r
2810 }\r
2811 \r
2812 VOID\r
2813 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2814 {\r
2815   int i;\r
2816   for (i=0; i<2; i++) {\r
2817     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2818       DrawHighlightOnDC(hdc, TRUE,\r
2819                         h->sq[i].x, h->sq[i].y,\r
2820                         pen);\r
2821   }\r
2822 }\r
2823 \r
2824 /* Note: sqcolor is used only in monoMode */\r
2825 /* Note that this code is largely duplicated in woptions.c,\r
2826    function DrawSampleSquare, so that needs to be updated too */\r
2827 VOID\r
2828 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2829 {\r
2830   HBITMAP oldBitmap;\r
2831   HBRUSH oldBrush;\r
2832   int tmpSize;\r
2833 \r
2834   if (appData.blindfold) return;\r
2835 \r
2836   /* [AS] Use font-based pieces if needed */\r
2837   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2838     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2839     CreatePiecesFromFont();\r
2840 \r
2841     if( fontBitmapSquareSize == squareSize ) {\r
2842         int index = TranslatePieceToFontPiece(piece);\r
2843 \r
2844         SelectObject( tmphdc, hPieceMask[ index ] );\r
2845 \r
2846       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2847         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2848       else\r
2849         BitBlt( hdc,\r
2850             x, y,\r
2851             squareSize, squareSize,\r
2852             tmphdc,\r
2853             0, 0,\r
2854             SRCAND );\r
2855 \r
2856         SelectObject( tmphdc, hPieceFace[ index ] );\r
2857 \r
2858       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2859         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2860       else\r
2861         BitBlt( hdc,\r
2862             x, y,\r
2863             squareSize, squareSize,\r
2864             tmphdc,\r
2865             0, 0,\r
2866             SRCPAINT );\r
2867 \r
2868         return;\r
2869     }\r
2870   }\r
2871 \r
2872   if (appData.monoMode) {\r
2873     SelectObject(tmphdc, PieceBitmap(piece, \r
2874       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2875     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2876            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2877   } else {\r
2878     tmpSize = squareSize;\r
2879     if(minorSize &&\r
2880         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2881          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2882       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2883       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2884       x += (squareSize - minorSize)>>1;\r
2885       y += squareSize - minorSize - 2;\r
2886       tmpSize = minorSize;\r
2887     }\r
2888     if (color || appData.allWhite ) {\r
2889       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2890       if( color )\r
2891               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2892       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2893       if(appData.upsideDown && color==flipView)\r
2894         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2895       else\r
2896         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2897       /* Use black for outline of white pieces */\r
2898       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2899       if(appData.upsideDown && color==flipView)\r
2900         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2901       else\r
2902         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2903     } else {\r
2904       /* Use square color for details of black pieces */\r
2905       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2906       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2907       if(appData.upsideDown && !flipView)\r
2908         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2909       else\r
2910         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2911     }\r
2912     SelectObject(hdc, oldBrush);\r
2913     SelectObject(tmphdc, oldBitmap);\r
2914   }\r
2915 }\r
2916 \r
2917 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2918 int GetBackTextureMode( int algo )\r
2919 {\r
2920     int result = BACK_TEXTURE_MODE_DISABLED;\r
2921 \r
2922     switch( algo ) \r
2923     {\r
2924         case BACK_TEXTURE_MODE_PLAIN:\r
2925             result = 1; /* Always use identity map */\r
2926             break;\r
2927         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2928             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2929             break;\r
2930     }\r
2931 \r
2932     return result;\r
2933 }\r
2934 \r
2935 /* \r
2936     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2937     to handle redraws cleanly (as random numbers would always be different).\r
2938 */\r
2939 VOID RebuildTextureSquareInfo()\r
2940 {\r
2941     BITMAP bi;\r
2942     int lite_w = 0;\r
2943     int lite_h = 0;\r
2944     int dark_w = 0;\r
2945     int dark_h = 0;\r
2946     int row;\r
2947     int col;\r
2948 \r
2949     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2950 \r
2951     if( liteBackTexture != NULL ) {\r
2952         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2953             lite_w = bi.bmWidth;\r
2954             lite_h = bi.bmHeight;\r
2955         }\r
2956     }\r
2957 \r
2958     if( darkBackTexture != NULL ) {\r
2959         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2960             dark_w = bi.bmWidth;\r
2961             dark_h = bi.bmHeight;\r
2962         }\r
2963     }\r
2964 \r
2965     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2966         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2967             if( (col + row) & 1 ) {\r
2968                 /* Lite square */\r
2969                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2970                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2971                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2972                   else\r
2973                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2974                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2975                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2976                   else\r
2977                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2978                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2979                 }\r
2980             }\r
2981             else {\r
2982                 /* Dark square */\r
2983                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2984                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2985                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2986                   else\r
2987                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2988                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2989                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2990                   else\r
2991                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2992                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2993                 }\r
2994             }\r
2995         }\r
2996     }\r
2997 }\r
2998 \r
2999 /* [AS] Arrow highlighting support */\r
3000 \r
3001 static double A_WIDTH = 5; /* Width of arrow body */\r
3002 \r
3003 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3004 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3005 \r
3006 static double Sqr( double x )\r
3007 {\r
3008     return x*x;\r
3009 }\r
3010 \r
3011 static int Round( double x )\r
3012 {\r
3013     return (int) (x + 0.5);\r
3014 }\r
3015 \r
3016 /* Draw an arrow between two points using current settings */\r
3017 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3018 {\r
3019     POINT arrow[7];\r
3020     double dx, dy, j, k, x, y;\r
3021 \r
3022     if( d_x == s_x ) {\r
3023         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3024 \r
3025         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3026         arrow[0].y = s_y;\r
3027 \r
3028         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3029         arrow[1].y = d_y - h;\r
3030 \r
3031         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3032         arrow[2].y = d_y - h;\r
3033 \r
3034         arrow[3].x = d_x;\r
3035         arrow[3].y = d_y;\r
3036 \r
3037         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3038         arrow[5].y = d_y - h;\r
3039 \r
3040         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3041         arrow[4].y = d_y - h;\r
3042 \r
3043         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3044         arrow[6].y = s_y;\r
3045     }\r
3046     else if( d_y == s_y ) {\r
3047         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3048 \r
3049         arrow[0].x = s_x;\r
3050         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3051 \r
3052         arrow[1].x = d_x - w;\r
3053         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3054 \r
3055         arrow[2].x = d_x - w;\r
3056         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3057 \r
3058         arrow[3].x = d_x;\r
3059         arrow[3].y = d_y;\r
3060 \r
3061         arrow[5].x = d_x - w;\r
3062         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3063 \r
3064         arrow[4].x = d_x - w;\r
3065         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3066 \r
3067         arrow[6].x = s_x;\r
3068         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3069     }\r
3070     else {\r
3071         /* [AS] Needed a lot of paper for this! :-) */\r
3072         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3073         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3074   \r
3075         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3076 \r
3077         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3078 \r
3079         x = s_x;\r
3080         y = s_y;\r
3081 \r
3082         arrow[0].x = Round(x - j);\r
3083         arrow[0].y = Round(y + j*dx);\r
3084 \r
3085         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3086         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3087 \r
3088         if( d_x > s_x ) {\r
3089             x = (double) d_x - k;\r
3090             y = (double) d_y - k*dy;\r
3091         }\r
3092         else {\r
3093             x = (double) d_x + k;\r
3094             y = (double) d_y + k*dy;\r
3095         }\r
3096 \r
3097         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3098 \r
3099         arrow[6].x = Round(x - j);\r
3100         arrow[6].y = Round(y + j*dx);\r
3101 \r
3102         arrow[2].x = Round(arrow[6].x + 2*j);\r
3103         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3104 \r
3105         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3106         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3107 \r
3108         arrow[4].x = d_x;\r
3109         arrow[4].y = d_y;\r
3110 \r
3111         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3112         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3113     }\r
3114 \r
3115     Polygon( hdc, arrow, 7 );\r
3116 }\r
3117 \r
3118 /* [AS] Draw an arrow between two squares */\r
3119 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3120 {\r
3121     int s_x, s_y, d_x, d_y;\r
3122     HPEN hpen;\r
3123     HPEN holdpen;\r
3124     HBRUSH hbrush;\r
3125     HBRUSH holdbrush;\r
3126     LOGBRUSH stLB;\r
3127 \r
3128     if( s_col == d_col && s_row == d_row ) {\r
3129         return;\r
3130     }\r
3131 \r
3132     /* Get source and destination points */\r
3133     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3134     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3135 \r
3136     if( d_y > s_y ) {\r
3137         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3138     }\r
3139     else if( d_y < s_y ) {\r
3140         d_y += squareSize / 2 + squareSize / 4;\r
3141     }\r
3142     else {\r
3143         d_y += squareSize / 2;\r
3144     }\r
3145 \r
3146     if( d_x > s_x ) {\r
3147         d_x += squareSize / 2 - squareSize / 4;\r
3148     }\r
3149     else if( d_x < s_x ) {\r
3150         d_x += squareSize / 2 + squareSize / 4;\r
3151     }\r
3152     else {\r
3153         d_x += squareSize / 2;\r
3154     }\r
3155 \r
3156     s_x += squareSize / 2;\r
3157     s_y += squareSize / 2;\r
3158 \r
3159     /* Adjust width */\r
3160     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3161 \r
3162     /* Draw */\r
3163     stLB.lbStyle = BS_SOLID;\r
3164     stLB.lbColor = appData.highlightArrowColor;\r
3165     stLB.lbHatch = 0;\r
3166 \r
3167     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3168     holdpen = SelectObject( hdc, hpen );\r
3169     hbrush = CreateBrushIndirect( &stLB );\r
3170     holdbrush = SelectObject( hdc, hbrush );\r
3171 \r
3172     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3173 \r
3174     SelectObject( hdc, holdpen );\r
3175     SelectObject( hdc, holdbrush );\r
3176     DeleteObject( hpen );\r
3177     DeleteObject( hbrush );\r
3178 }\r
3179 \r
3180 BOOL HasHighlightInfo()\r
3181 {\r
3182     BOOL result = FALSE;\r
3183 \r
3184     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3185         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3186     {\r
3187         result = TRUE;\r
3188     }\r
3189 \r
3190     return result;\r
3191 }\r
3192 \r
3193 BOOL IsDrawArrowEnabled()\r
3194 {\r
3195     BOOL result = FALSE;\r
3196 \r
3197     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3198         result = TRUE;\r
3199     }\r
3200 \r
3201     return result;\r
3202 }\r
3203 \r
3204 VOID DrawArrowHighlight( HDC hdc )\r
3205 {\r
3206     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3207         DrawArrowBetweenSquares( hdc,\r
3208             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3209             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3210     }\r
3211 }\r
3212 \r
3213 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3214 {\r
3215     HRGN result = NULL;\r
3216 \r
3217     if( HasHighlightInfo() ) {\r
3218         int x1, y1, x2, y2;\r
3219         int sx, sy, dx, dy;\r
3220 \r
3221         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3222         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3223 \r
3224         sx = MIN( x1, x2 );\r
3225         sy = MIN( y1, y2 );\r
3226         dx = MAX( x1, x2 ) + squareSize;\r
3227         dy = MAX( y1, y2 ) + squareSize;\r
3228 \r
3229         result = CreateRectRgn( sx, sy, dx, dy );\r
3230     }\r
3231 \r
3232     return result;\r
3233 }\r
3234 \r
3235 /*\r
3236     Warning: this function modifies the behavior of several other functions. \r
3237     \r
3238     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3239     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3240     repaint is scattered all over the place, which is not good for features such as\r
3241     "arrow highlighting" that require a full repaint of the board.\r
3242 \r
3243     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3244     user interaction, when speed is not so important) but especially to avoid errors\r
3245     in the displayed graphics.\r
3246 \r
3247     In such patched places, I always try refer to this function so there is a single\r
3248     place to maintain knowledge.\r
3249     \r
3250     To restore the original behavior, just return FALSE unconditionally.\r
3251 */\r
3252 BOOL IsFullRepaintPreferrable()\r
3253 {\r
3254     BOOL result = FALSE;\r
3255 \r
3256     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3257         /* Arrow may appear on the board */\r
3258         result = TRUE;\r
3259     }\r
3260 \r
3261     return result;\r
3262 }\r
3263 \r
3264 /* \r
3265     This function is called by DrawPosition to know whether a full repaint must\r
3266     be forced or not.\r
3267 \r
3268     Only DrawPosition may directly call this function, which makes use of \r
3269     some state information. Other function should call DrawPosition specifying \r
3270     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3271 */\r
3272 BOOL DrawPositionNeedsFullRepaint()\r
3273 {\r
3274     BOOL result = FALSE;\r
3275 \r
3276     /* \r
3277         Probably a slightly better policy would be to trigger a full repaint\r
3278         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3279         but animation is fast enough that it's difficult to notice.\r
3280     */\r
3281     if( animInfo.piece == EmptySquare ) {\r
3282         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3283             result = TRUE;\r
3284         }\r
3285     }\r
3286 \r
3287     return result;\r
3288 }\r
3289 \r
3290 VOID\r
3291 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3292 {\r
3293   int row, column, x, y, square_color, piece_color;\r
3294   ChessSquare piece;\r
3295   HBRUSH oldBrush;\r
3296   HDC texture_hdc = NULL;\r
3297 \r
3298   /* [AS] Initialize background textures if needed */\r
3299   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3300       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3301       if( backTextureSquareSize != squareSize \r
3302        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3303           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3304           backTextureSquareSize = squareSize;\r
3305           RebuildTextureSquareInfo();\r
3306       }\r
3307 \r
3308       texture_hdc = CreateCompatibleDC( hdc );\r
3309   }\r
3310 \r
3311   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3312     for (column = 0; column < BOARD_WIDTH; column++) {\r
3313   \r
3314       SquareToPos(row, column, &x, &y);\r
3315 \r
3316       piece = board[row][column];\r
3317 \r
3318       square_color = ((column + row) % 2) == 1;\r
3319       if( gameInfo.variant == VariantXiangqi ) {\r
3320           square_color = !InPalace(row, column);\r
3321           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3322           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3323       }\r
3324       piece_color = (int) piece < (int) BlackPawn;\r
3325 \r
3326 \r
3327       /* [HGM] holdings file: light square or black */\r
3328       if(column == BOARD_LEFT-2) {\r
3329             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3330                 square_color = 1;\r
3331             else {\r
3332                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3333                 continue;\r
3334             }\r
3335       } else\r
3336       if(column == BOARD_RGHT + 1 ) {\r
3337             if( row < gameInfo.holdingsSize )\r
3338                 square_color = 1;\r
3339             else {\r
3340                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3341                 continue;\r
3342             }\r
3343       }\r
3344       if(column == BOARD_LEFT-1 ) /* left align */\r
3345             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3346       else if( column == BOARD_RGHT) /* right align */\r
3347             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3348       else\r
3349       if (appData.monoMode) {\r
3350         if (piece == EmptySquare) {\r
3351           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3352                  square_color ? WHITENESS : BLACKNESS);\r
3353         } else {\r
3354           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3355         }\r
3356       } \r
3357       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3358           /* [AS] Draw the square using a texture bitmap */\r
3359           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3360           int r = row, c = column; // [HGM] do not flip board in flipView\r
3361           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3362 \r
3363           DrawTile( x, y, \r
3364               squareSize, squareSize, \r
3365               hdc, \r
3366               texture_hdc,\r
3367               backTextureSquareInfo[r][c].mode,\r
3368               backTextureSquareInfo[r][c].x,\r
3369               backTextureSquareInfo[r][c].y );\r
3370 \r
3371           SelectObject( texture_hdc, hbm );\r
3372 \r
3373           if (piece != EmptySquare) {\r
3374               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3375           }\r
3376       }\r
3377       else {\r
3378         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3379 \r
3380         oldBrush = SelectObject(hdc, brush );\r
3381         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3382         SelectObject(hdc, oldBrush);\r
3383         if (piece != EmptySquare)\r
3384           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3385       }\r
3386     }\r
3387   }\r
3388 \r
3389   if( texture_hdc != NULL ) {\r
3390     DeleteDC( texture_hdc );\r
3391   }\r
3392 }\r
3393 \r
3394 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3395 void fputDW(FILE *f, int x)\r
3396 {\r
3397         fputc(x     & 255, f);\r
3398         fputc(x>>8  & 255, f);\r
3399         fputc(x>>16 & 255, f);\r
3400         fputc(x>>24 & 255, f);\r
3401 }\r
3402 \r
3403 #define MAX_CLIPS 200   /* more than enough */\r
3404 \r
3405 VOID\r
3406 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3407 {\r
3408 //  HBITMAP bufferBitmap;\r
3409   BITMAP bi;\r
3410 //  RECT Rect;\r
3411   HDC tmphdc;\r
3412   HBITMAP hbm;\r
3413   int w = 100, h = 50;\r
3414 \r
3415   if(logo == NULL) {\r
3416     if(!logoHeight) return;\r
3417     FillRect( hdc, &logoRect, whitePieceBrush );\r
3418   }\r
3419 //  GetClientRect(hwndMain, &Rect);\r
3420 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3421 //                                      Rect.bottom-Rect.top+1);\r
3422   tmphdc = CreateCompatibleDC(hdc);\r
3423   hbm = SelectObject(tmphdc, logo);\r
3424   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3425             w = bi.bmWidth;\r
3426             h = bi.bmHeight;\r
3427   }\r
3428   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3429                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3430   SelectObject(tmphdc, hbm);\r
3431   DeleteDC(tmphdc);\r
3432 }\r
3433 \r
3434 VOID\r
3435 DisplayLogos()\r
3436 {\r
3437   if(logoHeight) {\r
3438         HDC hdc = GetDC(hwndMain);\r
3439         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3440         if(appData.autoLogo) {\r
3441           \r
3442           switch(gameMode) { // pick logos based on game mode\r
3443             case IcsObserving:\r
3444                 whiteLogo = second.programLogo; // ICS logo\r
3445                 blackLogo = second.programLogo;\r
3446             default:\r
3447                 break;\r
3448             case IcsPlayingWhite:\r
3449                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3450                 blackLogo = second.programLogo; // ICS logo\r
3451                 break;\r
3452             case IcsPlayingBlack:\r
3453                 whiteLogo = second.programLogo; // ICS logo\r
3454                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3455                 break;\r
3456             case TwoMachinesPlay:\r
3457                 if(first.twoMachinesColor[0] == 'b') {\r
3458                     whiteLogo = second.programLogo;\r
3459                     blackLogo = first.programLogo;\r
3460                 }\r
3461                 break;\r
3462             case MachinePlaysWhite:\r
3463                 blackLogo = userLogo;\r
3464                 break;\r
3465             case MachinePlaysBlack:\r
3466                 whiteLogo = userLogo;\r
3467                 blackLogo = first.programLogo;\r
3468           }\r
3469         }\r
3470         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3471         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3472         ReleaseDC(hwndMain, hdc);\r
3473   }\r
3474 }\r
3475 \r
3476 void\r
3477 UpdateLogos(int display)\r
3478 { // called after loading new engine(s), in tourney or from menu\r
3479   LoadLogo(&first, 0, FALSE);\r
3480   LoadLogo(&second, 1, appData.icsActive);\r
3481   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3482   if(display) DisplayLogos();\r
3483 }\r
3484 \r
3485 static HDC hdcSeek;\r
3486 \r
3487 // [HGM] seekgraph\r
3488 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3489 {\r
3490     POINT stPt;\r
3491     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3492     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3493     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3494     SelectObject( hdcSeek, hp );\r
3495 }\r
3496 \r
3497 // front-end wrapper for drawing functions to do rectangles\r
3498 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3499 {\r
3500     HPEN hp;\r
3501     RECT rc;\r
3502 \r
3503     if (hdcSeek == NULL) {\r
3504     hdcSeek = GetDC(hwndMain);\r
3505       if (!appData.monoMode) {\r
3506         SelectPalette(hdcSeek, hPal, FALSE);\r
3507         RealizePalette(hdcSeek);\r
3508       }\r
3509     }\r
3510     hp = SelectObject( hdcSeek, gridPen );\r
3511     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3512     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3513     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3514     SelectObject( hdcSeek, hp );\r
3515 }\r
3516 \r
3517 // front-end wrapper for putting text in graph\r
3518 void DrawSeekText(char *buf, int x, int y)\r
3519 {\r
3520         SIZE stSize;\r
3521         SetBkMode( hdcSeek, TRANSPARENT );\r
3522         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3523         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3524 }\r
3525 \r
3526 void DrawSeekDot(int x, int y, int color)\r
3527 {\r
3528         int square = color & 0x80;\r
3529         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3530                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3531         color &= 0x7F;\r
3532         if(square)\r
3533             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3534                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3535         else\r
3536             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3537                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3538             SelectObject(hdcSeek, oldBrush);\r
3539 }\r
3540 \r
3541 VOID\r
3542 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3543 {\r
3544   static Board lastReq[2], lastDrawn[2];\r
3545   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3546   static int lastDrawnFlipView = 0;\r
3547   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3548   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3549   HDC tmphdc;\r
3550   HDC hdcmem;\r
3551   HBITMAP bufferBitmap;\r
3552   HBITMAP oldBitmap;\r
3553   RECT Rect;\r
3554   HRGN clips[MAX_CLIPS];\r
3555   ChessSquare dragged_piece = EmptySquare;\r
3556   int nr = twoBoards*partnerUp;\r
3557 \r
3558   /* I'm undecided on this - this function figures out whether a full\r
3559    * repaint is necessary on its own, so there's no real reason to have the\r
3560    * caller tell it that.  I think this can safely be set to FALSE - but\r
3561    * if we trust the callers not to request full repaints unnessesarily, then\r
3562    * we could skip some clipping work.  In other words, only request a full\r
3563    * redraw when the majority of pieces have changed positions (ie. flip, \r
3564    * gamestart and similar)  --Hawk\r
3565    */\r
3566   Boolean fullrepaint = repaint;\r
3567 \r
3568   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3569 \r
3570   if( DrawPositionNeedsFullRepaint() ) {\r
3571       fullrepaint = TRUE;\r
3572   }\r
3573 \r
3574   if (board == NULL) {\r
3575     if (!lastReqValid[nr]) {\r
3576       return;\r
3577     }\r
3578     board = lastReq[nr];\r
3579   } else {\r
3580     CopyBoard(lastReq[nr], board);\r
3581     lastReqValid[nr] = 1;\r
3582   }\r
3583 \r
3584   if (doingSizing) {\r
3585     return;\r
3586   }\r
3587 \r
3588   if (IsIconic(hwndMain)) {\r
3589     return;\r
3590   }\r
3591 \r
3592   if (hdc == NULL) {\r
3593     hdc = GetDC(hwndMain);\r
3594     if (!appData.monoMode) {\r
3595       SelectPalette(hdc, hPal, FALSE);\r
3596       RealizePalette(hdc);\r
3597     }\r
3598     releaseDC = TRUE;\r
3599   } else {\r
3600     releaseDC = FALSE;\r
3601   }\r
3602 \r
3603   /* Create some work-DCs */\r
3604   hdcmem = CreateCompatibleDC(hdc);\r
3605   tmphdc = CreateCompatibleDC(hdc);\r
3606 \r
3607   /* If dragging is in progress, we temporarely remove the piece */\r
3608   /* [HGM] or temporarily decrease count if stacked              */\r
3609   /*       !! Moved to before board compare !!                   */\r
3610   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3611     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3612     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3613             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3614         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3615     } else \r
3616     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3617             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3618         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3619     } else \r
3620         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3621   }\r
3622 \r
3623   /* Figure out which squares need updating by comparing the \r
3624    * newest board with the last drawn board and checking if\r
3625    * flipping has changed.\r
3626    */\r
3627   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3628     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3629       for (column = 0; column < BOARD_WIDTH; column++) {\r
3630         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3631           SquareToPos(row, column, &x, &y);\r
3632           clips[num_clips++] =\r
3633             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3634         }\r
3635       }\r
3636     }\r
3637    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3638     for (i=0; i<2; i++) {\r
3639       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3640           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3641         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3642             lastDrawnHighlight.sq[i].y >= 0) {\r
3643           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3644                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3645           clips[num_clips++] =\r
3646             CreateRectRgn(x - lineGap, y - lineGap, \r
3647                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3648         }\r
3649         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3650           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3651           clips[num_clips++] =\r
3652             CreateRectRgn(x - lineGap, y - lineGap, \r
3653                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3654         }\r
3655       }\r
3656     }\r
3657     for (i=0; i<2; i++) {\r
3658       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3659           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3660         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3661             lastDrawnPremove.sq[i].y >= 0) {\r
3662           SquareToPos(lastDrawnPremove.sq[i].y,\r
3663                       lastDrawnPremove.sq[i].x, &x, &y);\r
3664           clips[num_clips++] =\r
3665             CreateRectRgn(x - lineGap, y - lineGap, \r
3666                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3667         }\r
3668         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3669             premoveHighlightInfo.sq[i].y >= 0) {\r
3670           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3671                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3672           clips[num_clips++] =\r
3673             CreateRectRgn(x - lineGap, y - lineGap, \r
3674                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3675         }\r
3676       }\r
3677     }\r
3678    } else { // nr == 1\r
3679         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3680         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3681         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3682         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3683       for (i=0; i<2; i++) {\r
3684         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3685             partnerHighlightInfo.sq[i].y >= 0) {\r
3686           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3687                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3688           clips[num_clips++] =\r
3689             CreateRectRgn(x - lineGap, y - lineGap, \r
3690                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3691         }\r
3692         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3693             oldPartnerHighlight.sq[i].y >= 0) {\r
3694           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3695                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3696           clips[num_clips++] =\r
3697             CreateRectRgn(x - lineGap, y - lineGap, \r
3698                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3699         }\r
3700       }\r
3701    }\r
3702   } else {\r
3703     fullrepaint = TRUE;\r
3704   }\r
3705 \r
3706   /* Create a buffer bitmap - this is the actual bitmap\r
3707    * being written to.  When all the work is done, we can\r
3708    * copy it to the real DC (the screen).  This avoids\r
3709    * the problems with flickering.\r
3710    */\r
3711   GetClientRect(hwndMain, &Rect);\r
3712   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3713                                         Rect.bottom-Rect.top+1);\r
3714   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3715   if (!appData.monoMode) {\r
3716     SelectPalette(hdcmem, hPal, FALSE);\r
3717   }\r
3718 \r
3719   /* Create clips for dragging */\r
3720   if (!fullrepaint) {\r
3721     if (dragInfo.from.x >= 0) {\r
3722       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3723       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3724     }\r
3725     if (dragInfo.start.x >= 0) {\r
3726       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3727       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3728     }\r
3729     if (dragInfo.pos.x >= 0) {\r
3730       x = dragInfo.pos.x - squareSize / 2;\r
3731       y = dragInfo.pos.y - squareSize / 2;\r
3732       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3733     }\r
3734     if (dragInfo.lastpos.x >= 0) {\r
3735       x = dragInfo.lastpos.x - squareSize / 2;\r
3736       y = dragInfo.lastpos.y - squareSize / 2;\r
3737       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3738     }\r
3739   }\r
3740 \r
3741   /* Are we animating a move?  \r
3742    * If so, \r
3743    *   - remove the piece from the board (temporarely)\r
3744    *   - calculate the clipping region\r
3745    */\r
3746   if (!fullrepaint) {\r
3747     if (animInfo.piece != EmptySquare) {\r
3748       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3749       x = boardRect.left + animInfo.lastpos.x;\r
3750       y = boardRect.top + animInfo.lastpos.y;\r
3751       x2 = boardRect.left + animInfo.pos.x;\r
3752       y2 = boardRect.top + animInfo.pos.y;\r
3753       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3754       /* Slight kludge.  The real problem is that after AnimateMove is\r
3755          done, the position on the screen does not match lastDrawn.\r
3756          This currently causes trouble only on e.p. captures in\r
3757          atomic, where the piece moves to an empty square and then\r
3758          explodes.  The old and new positions both had an empty square\r
3759          at the destination, but animation has drawn a piece there and\r
3760          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3761       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3762     }\r
3763   }\r
3764 \r
3765   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3766   if (num_clips == 0)\r
3767     fullrepaint = TRUE;\r
3768 \r
3769   /* Set clipping on the memory DC */\r
3770   if (!fullrepaint) {\r
3771     SelectClipRgn(hdcmem, clips[0]);\r
3772     for (x = 1; x < num_clips; x++) {\r
3773       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3774         abort();  // this should never ever happen!\r
3775     }\r
3776   }\r
3777 \r
3778   /* Do all the drawing to the memory DC */\r
3779   if(explodeInfo.radius) { // [HGM] atomic\r
3780         HBRUSH oldBrush;\r
3781         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3782         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3783         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3784         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3785         x += squareSize/2;\r
3786         y += squareSize/2;\r
3787         if(!fullrepaint) {\r
3788           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3789           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3790         }\r
3791         DrawGridOnDC(hdcmem);\r
3792         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3793         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3794         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3795         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3796         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3797         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3798         SelectObject(hdcmem, oldBrush);\r
3799   } else {\r
3800     DrawGridOnDC(hdcmem);\r
3801     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3802         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3803         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3804     } else {\r
3805         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3806         oldPartnerHighlight = partnerHighlightInfo;\r
3807     }\r
3808     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3809   }\r
3810   if(nr == 0) // [HGM] dual: markers only on left board\r
3811   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3812     for (column = 0; column < BOARD_WIDTH; column++) {\r
3813         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3814             HBRUSH oldBrush = SelectObject(hdcmem, \r
3815                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3816             SquareToPos(row, column, &x, &y);\r
3817             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3818                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3819             SelectObject(hdcmem, oldBrush);\r
3820         }\r
3821     }\r
3822   }\r
3823 \r
3824   if( appData.highlightMoveWithArrow ) {\r
3825     DrawArrowHighlight(hdcmem);\r
3826   }\r
3827 \r
3828   DrawCoordsOnDC(hdcmem);\r
3829 \r
3830   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3831                  /* to make sure lastDrawn contains what is actually drawn */\r
3832 \r
3833   /* Put the dragged piece back into place and draw it (out of place!) */\r
3834     if (dragged_piece != EmptySquare) {\r
3835     /* [HGM] or restack */\r
3836     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3837                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3838     else\r
3839     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3840                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3841     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3842     x = dragInfo.pos.x - squareSize / 2;\r
3843     y = dragInfo.pos.y - squareSize / 2;\r
3844     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3845                   ((int) dragInfo.piece < (int) BlackPawn), \r
3846                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3847   }   \r
3848   \r
3849   /* Put the animated piece back into place and draw it */\r
3850   if (animInfo.piece != EmptySquare) {\r
3851     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3852     x = boardRect.left + animInfo.pos.x;\r
3853     y = boardRect.top + animInfo.pos.y;\r
3854     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3855                   ((int) animInfo.piece < (int) BlackPawn),\r
3856                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3857   }\r
3858 \r
3859   /* Release the bufferBitmap by selecting in the old bitmap \r
3860    * and delete the memory DC\r
3861    */\r
3862   SelectObject(hdcmem, oldBitmap);\r
3863   DeleteDC(hdcmem);\r
3864 \r
3865   /* Set clipping on the target DC */\r
3866   if (!fullrepaint) {\r
3867     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3868         RECT rect;\r
3869         GetRgnBox(clips[x], &rect);\r
3870         DeleteObject(clips[x]);\r
3871         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3872                           rect.right + wpMain.width/2, rect.bottom);\r
3873     }\r
3874     SelectClipRgn(hdc, clips[0]);\r
3875     for (x = 1; x < num_clips; x++) {\r
3876       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3877         abort();   // this should never ever happen!\r
3878     } \r
3879   }\r
3880 \r
3881   /* Copy the new bitmap onto the screen in one go.\r
3882    * This way we avoid any flickering\r
3883    */\r
3884   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3885   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3886          boardRect.right - boardRect.left,\r
3887          boardRect.bottom - boardRect.top,\r
3888          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3889   if(saveDiagFlag) { \r
3890     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3891     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3892 \r
3893     GetObject(bufferBitmap, sizeof(b), &b);\r
3894     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3895         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3896         bih.biWidth = b.bmWidth;\r
3897         bih.biHeight = b.bmHeight;\r
3898         bih.biPlanes = 1;\r
3899         bih.biBitCount = b.bmBitsPixel;\r
3900         bih.biCompression = 0;\r
3901         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3902         bih.biXPelsPerMeter = 0;\r
3903         bih.biYPelsPerMeter = 0;\r
3904         bih.biClrUsed = 0;\r
3905         bih.biClrImportant = 0;\r
3906 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3907 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3908         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3909 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3910 \r
3911         wb = b.bmWidthBytes;\r
3912         // count colors\r
3913         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3914                 int k = ((int*) pData)[i];\r
3915                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3916                 if(j >= 16) break;\r
3917                 color[j] = k;\r
3918                 if(j >= nrColors) nrColors = j+1;\r
3919         }\r
3920         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3921                 INT p = 0;\r
3922                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3923                     for(w=0; w<(wb>>2); w+=2) {\r
3924                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3925                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3926                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3927                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3928                         pData[p++] = m | j<<4;\r
3929                     }\r
3930                     while(p&3) pData[p++] = 0;\r
3931                 }\r
3932                 fac = 3;\r
3933                 wb = ((wb+31)>>5)<<2;\r
3934         }\r
3935         // write BITMAPFILEHEADER\r
3936         fprintf(diagFile, "BM");\r
3937         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3938         fputDW(diagFile, 0);\r
3939         fputDW(diagFile, 0x36 + (fac?64:0));\r
3940         // write BITMAPINFOHEADER\r
3941         fputDW(diagFile, 40);\r
3942         fputDW(diagFile, b.bmWidth);\r
3943         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3944         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3945         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3946         fputDW(diagFile, 0);\r
3947         fputDW(diagFile, 0);\r
3948         fputDW(diagFile, 0);\r
3949         fputDW(diagFile, 0);\r
3950         fputDW(diagFile, 0);\r
3951         fputDW(diagFile, 0);\r
3952         // write color table\r
3953         if(fac)\r
3954         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3955         // write bitmap data\r
3956         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3957                 fputc(pData[i], diagFile);\r
3958         free(pData);\r
3959      }\r
3960   }\r
3961 \r
3962   SelectObject(tmphdc, oldBitmap);\r
3963 \r
3964   /* Massive cleanup */\r
3965   for (x = 0; x < num_clips; x++)\r
3966     DeleteObject(clips[x]);\r
3967 \r
3968   DeleteDC(tmphdc);\r
3969   DeleteObject(bufferBitmap);\r
3970 \r
3971   if (releaseDC) \r
3972     ReleaseDC(hwndMain, hdc);\r
3973   \r
3974   if (lastDrawnFlipView != flipView && nr == 0) {\r
3975     if (flipView)\r
3976       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3977     else\r
3978       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3979   }\r
3980 \r
3981 /*  CopyBoard(lastDrawn, board);*/\r
3982   lastDrawnHighlight = highlightInfo;\r
3983   lastDrawnPremove   = premoveHighlightInfo;\r
3984   lastDrawnFlipView = flipView;\r
3985   lastDrawnValid[nr] = 1;\r
3986 }\r
3987 \r
3988 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3989 int\r
3990 SaveDiagram(f)\r
3991      FILE *f;\r
3992 {\r
3993     saveDiagFlag = 1; diagFile = f;\r
3994     HDCDrawPosition(NULL, TRUE, NULL);\r
3995     saveDiagFlag = 0;\r
3996 \r
3997     fclose(f);\r
3998     return TRUE;\r
3999 }\r
4000 \r
4001 \r
4002 /*---------------------------------------------------------------------------*\\r
4003 | CLIENT PAINT PROCEDURE\r
4004 |   This is the main event-handler for the WM_PAINT message.\r
4005 |\r
4006 \*---------------------------------------------------------------------------*/\r
4007 VOID\r
4008 PaintProc(HWND hwnd)\r
4009 {\r
4010   HDC         hdc;\r
4011   PAINTSTRUCT ps;\r
4012   HFONT       oldFont;\r
4013 \r
4014   if((hdc = BeginPaint(hwnd, &ps))) {\r
4015     if (IsIconic(hwnd)) {\r
4016       DrawIcon(hdc, 2, 2, iconCurrent);\r
4017     } else {\r
4018       if (!appData.monoMode) {\r
4019         SelectPalette(hdc, hPal, FALSE);\r
4020         RealizePalette(hdc);\r
4021       }\r
4022       HDCDrawPosition(hdc, 1, NULL);\r
4023       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4024         flipView = !flipView; partnerUp = !partnerUp;\r
4025         HDCDrawPosition(hdc, 1, NULL);\r
4026         flipView = !flipView; partnerUp = !partnerUp;\r
4027       }\r
4028       oldFont =\r
4029         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4030       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4031                  ETO_CLIPPED|ETO_OPAQUE,\r
4032                  &messageRect, messageText, strlen(messageText), NULL);\r
4033       SelectObject(hdc, oldFont);\r
4034       DisplayBothClocks();\r
4035       DisplayLogos();\r
4036     }\r
4037     EndPaint(hwnd,&ps);\r
4038   }\r
4039 \r
4040   return;\r
4041 }\r
4042 \r
4043 \r
4044 /*\r
4045  * If the user selects on a border boundary, return -1; if off the board,\r
4046  *   return -2.  Otherwise map the event coordinate to the square.\r
4047  * The offset boardRect.left or boardRect.top must already have been\r
4048  *   subtracted from x.\r
4049  */\r
4050 int EventToSquare(x, limit)\r
4051      int x, limit;\r
4052 {\r
4053   if (x <= 0)\r
4054     return -2;\r
4055   if (x < lineGap)\r
4056     return -1;\r
4057   x -= lineGap;\r
4058   if ((x % (squareSize + lineGap)) >= squareSize)\r
4059     return -1;\r
4060   x /= (squareSize + lineGap);\r
4061     if (x >= limit)\r
4062     return -2;\r
4063   return x;\r
4064 }\r
4065 \r
4066 typedef struct {\r
4067   char piece;\r
4068   int command;\r
4069   char* name;\r
4070 } DropEnable;\r
4071 \r
4072 DropEnable dropEnables[] = {\r
4073   { 'P', DP_Pawn, N_("Pawn") },\r
4074   { 'N', DP_Knight, N_("Knight") },\r
4075   { 'B', DP_Bishop, N_("Bishop") },\r
4076   { 'R', DP_Rook, N_("Rook") },\r
4077   { 'Q', DP_Queen, N_("Queen") },\r
4078 };\r
4079 \r
4080 VOID\r
4081 SetupDropMenu(HMENU hmenu)\r
4082 {\r
4083   int i, count, enable;\r
4084   char *p;\r
4085   extern char white_holding[], black_holding[];\r
4086   char item[MSG_SIZ];\r
4087 \r
4088   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4089     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4090                dropEnables[i].piece);\r
4091     count = 0;\r
4092     while (p && *p++ == dropEnables[i].piece) count++;\r
4093       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4094     enable = count > 0 || !appData.testLegality\r
4095       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4096                       && !appData.icsActive);\r
4097     ModifyMenu(hmenu, dropEnables[i].command,\r
4098                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4099                dropEnables[i].command, item);\r
4100   }\r
4101 }\r
4102 \r
4103 void DragPieceBegin(int x, int y, Boolean instantly)\r
4104 {\r
4105       dragInfo.lastpos.x = boardRect.left + x;\r
4106       dragInfo.lastpos.y = boardRect.top + y;\r
4107       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4108       dragInfo.from.x = fromX;\r
4109       dragInfo.from.y = fromY;\r
4110       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4111       dragInfo.start = dragInfo.from;\r
4112       SetCapture(hwndMain);\r
4113 }\r
4114 \r
4115 void DragPieceEnd(int x, int y)\r
4116 {\r
4117     ReleaseCapture();\r
4118     dragInfo.start.x = dragInfo.start.y = -1;\r
4119     dragInfo.from = dragInfo.start;\r
4120     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4121 }\r
4122 \r
4123 void ChangeDragPiece(ChessSquare piece)\r
4124 {\r
4125     dragInfo.piece = piece;\r
4126 }\r
4127 \r
4128 /* Event handler for mouse messages */\r
4129 VOID\r
4130 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4131 {\r
4132   int x, y, menuNr;\r
4133   POINT pt;\r
4134   static int recursive = 0;\r
4135   HMENU hmenu;\r
4136   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4137 \r
4138   if (recursive) {\r
4139     if (message == WM_MBUTTONUP) {\r
4140       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4141          to the middle button: we simulate pressing the left button too!\r
4142          */\r
4143       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4144       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4145     }\r
4146     return;\r
4147   }\r
4148   recursive++;\r
4149   \r
4150   pt.x = LOWORD(lParam);\r
4151   pt.y = HIWORD(lParam);\r
4152   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4153   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4154   if (!flipView && y >= 0) {\r
4155     y = BOARD_HEIGHT - 1 - y;\r
4156   }\r
4157   if (flipView && x >= 0) {\r
4158     x = BOARD_WIDTH - 1 - x;\r
4159   }\r
4160 \r
4161   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4162 \r
4163   switch (message) {\r
4164   case WM_LBUTTONDOWN:\r
4165       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4166         ClockClick(flipClock); break;\r
4167       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4168         ClockClick(!flipClock); break;\r
4169       }\r
4170       dragInfo.start.x = dragInfo.start.y = -1;\r
4171       dragInfo.from = dragInfo.start;\r
4172     if(fromX == -1 && frozen) { // not sure where this is for\r
4173                 fromX = fromY = -1; \r
4174       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4175       break;\r
4176     }\r
4177       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4178       DrawPosition(TRUE, NULL);\r
4179     break;\r
4180 \r
4181   case WM_LBUTTONUP:\r
4182       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4183       DrawPosition(TRUE, NULL);\r
4184     break;\r
4185 \r
4186   case WM_MOUSEMOVE:\r
4187     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4188     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4189     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4190     if ((appData.animateDragging || appData.highlightDragging)\r
4191         && (wParam & MK_LBUTTON)\r
4192         && dragInfo.from.x >= 0) \r
4193     {\r
4194       BOOL full_repaint = FALSE;\r
4195 \r
4196       if (appData.animateDragging) {\r
4197         dragInfo.pos = pt;\r
4198       }\r
4199       if (appData.highlightDragging) {\r
4200         SetHighlights(fromX, fromY, x, y);\r
4201         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4202             full_repaint = TRUE;\r
4203         }\r
4204       }\r
4205       \r
4206       DrawPosition( full_repaint, NULL);\r
4207       \r
4208       dragInfo.lastpos = dragInfo.pos;\r
4209     }\r
4210     break;\r
4211 \r
4212   case WM_MOUSEWHEEL: // [DM]\r
4213     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4214        /* Mouse Wheel is being rolled forward\r
4215         * Play moves forward\r
4216         */\r
4217        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4218                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4219        /* Mouse Wheel is being rolled backward\r
4220         * Play moves backward\r
4221         */\r
4222        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4223                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4224     }\r
4225     break;\r
4226 \r
4227   case WM_MBUTTONUP:\r
4228   case WM_RBUTTONUP:\r
4229     ReleaseCapture();\r
4230     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4231     break;\r
4232  \r
4233   case WM_MBUTTONDOWN:\r
4234   case WM_RBUTTONDOWN:\r
4235     ErrorPopDown();\r
4236     ReleaseCapture();\r
4237     fromX = fromY = -1;\r
4238     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4239     dragInfo.start.x = dragInfo.start.y = -1;\r
4240     dragInfo.from = dragInfo.start;\r
4241     dragInfo.lastpos = dragInfo.pos;\r
4242     if (appData.highlightDragging) {\r
4243       ClearHighlights();\r
4244     }\r
4245     if(y == -2) {\r
4246       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4247       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4248           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4249       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4250           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4251       }\r
4252       break;\r
4253     }\r
4254     DrawPosition(TRUE, NULL);\r
4255 \r
4256     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4257     switch (menuNr) {\r
4258     case 0:\r
4259       if (message == WM_MBUTTONDOWN) {\r
4260         buttonCount = 3;  /* even if system didn't think so */\r
4261         if (wParam & MK_SHIFT) \r
4262           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4263         else\r
4264           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4265       } else { /* message == WM_RBUTTONDOWN */\r
4266         /* Just have one menu, on the right button.  Windows users don't\r
4267            think to try the middle one, and sometimes other software steals\r
4268            it, or it doesn't really exist. */\r
4269         if(gameInfo.variant != VariantShogi)\r
4270             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4271         else\r
4272             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4273       }\r
4274       break;\r
4275     case 2:\r
4276       SetCapture(hwndMain);\r
4277       break;\r
4278     case 1:\r
4279       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4280       SetupDropMenu(hmenu);\r
4281       MenuPopup(hwnd, pt, hmenu, -1);\r
4282     default:\r
4283       break;\r
4284     }\r
4285     break;\r
4286   }\r
4287 \r
4288   recursive--;\r
4289 }\r
4290 \r
4291 /* Preprocess messages for buttons in main window */\r
4292 LRESULT CALLBACK\r
4293 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4294 {\r
4295   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4296   int i, dir;\r
4297 \r
4298   for (i=0; i<N_BUTTONS; i++) {\r
4299     if (buttonDesc[i].id == id) break;\r
4300   }\r
4301   if (i == N_BUTTONS) return 0;\r
4302   switch (message) {\r
4303   case WM_KEYDOWN:\r
4304     switch (wParam) {\r
4305     case VK_LEFT:\r
4306     case VK_RIGHT:\r
4307       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4308       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4309       return TRUE;\r
4310     }\r
4311     break;\r
4312   case WM_CHAR:\r
4313     switch (wParam) {\r
4314     case '\r':\r
4315       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4316       return TRUE;\r
4317     default:\r
4318       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4319         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4320         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4321         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4322         SetFocus(h);\r
4323         SendMessage(h, WM_CHAR, wParam, lParam);\r
4324         return TRUE;\r
4325       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4326         TypeInEvent((char)wParam);\r
4327       }\r
4328       break;\r
4329     }\r
4330     break;\r
4331   }\r
4332   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4333 }\r
4334 \r
4335 /* Process messages for Promotion dialog box */\r
4336 LRESULT CALLBACK\r
4337 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4338 {\r
4339   char promoChar;\r
4340 \r
4341   switch (message) {\r
4342   case WM_INITDIALOG: /* message: initialize dialog box */\r
4343     /* Center the dialog over the application window */\r
4344     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4345     Translate(hDlg, DLG_PromotionKing);\r
4346     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4347       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4348        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4349        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4350                SW_SHOW : SW_HIDE);\r
4351     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4352     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4353        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4354          PieceToChar(WhiteAngel) != '~') ||\r
4355         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4356          PieceToChar(BlackAngel) != '~')   ) ?\r
4357                SW_SHOW : SW_HIDE);\r
4358     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4359        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4360          PieceToChar(WhiteMarshall) != '~') ||\r
4361         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4362          PieceToChar(BlackMarshall) != '~')   ) ?\r
4363                SW_SHOW : SW_HIDE);\r
4364     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4365     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4366        gameInfo.variant != VariantShogi ?\r
4367                SW_SHOW : SW_HIDE);\r
4368     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4369        gameInfo.variant != VariantShogi ?\r
4370                SW_SHOW : SW_HIDE);\r
4371     if(gameInfo.variant == VariantShogi) {\r
4372         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4373         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4374         SetWindowText(hDlg, "Promote?");\r
4375     }\r
4376     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4377        gameInfo.variant == VariantSuper ?\r
4378                SW_SHOW : SW_HIDE);\r
4379     return TRUE;\r
4380 \r
4381   case WM_COMMAND: /* message: received a command */\r
4382     switch (LOWORD(wParam)) {\r
4383     case IDCANCEL:\r
4384       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4385       ClearHighlights();\r
4386       DrawPosition(FALSE, NULL);\r
4387       return TRUE;\r
4388     case PB_King:\r
4389       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4390       break;\r
4391     case PB_Queen:\r
4392       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4393       break;\r
4394     case PB_Rook:\r
4395       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4396       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4397       break;\r
4398     case PB_Bishop:\r
4399       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4400       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4401       break;\r
4402     case PB_Chancellor:\r
4403       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4404       break;\r
4405     case PB_Archbishop:\r
4406       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4407       break;\r
4408     case PB_Knight:\r
4409       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4410       break;\r
4411     default:\r
4412       return FALSE;\r
4413     }\r
4414     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4415     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4416     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4417     fromX = fromY = -1;\r
4418     if (!appData.highlightLastMove) {\r
4419       ClearHighlights();\r
4420       DrawPosition(FALSE, NULL);\r
4421     }\r
4422     return TRUE;\r
4423   }\r
4424   return FALSE;\r
4425 }\r
4426 \r
4427 /* Pop up promotion dialog */\r
4428 VOID\r
4429 PromotionPopup(HWND hwnd)\r
4430 {\r
4431   FARPROC lpProc;\r
4432 \r
4433   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4434   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4435     hwnd, (DLGPROC)lpProc);\r
4436   FreeProcInstance(lpProc);\r
4437 }\r
4438 \r
4439 void\r
4440 PromotionPopUp()\r
4441 {\r
4442   DrawPosition(TRUE, NULL);\r
4443   PromotionPopup(hwndMain);\r
4444 }\r
4445 \r
4446 /* Toggle ShowThinking */\r
4447 VOID\r
4448 ToggleShowThinking()\r
4449 {\r
4450   appData.showThinking = !appData.showThinking;\r
4451   ShowThinkingEvent();\r
4452 }\r
4453 \r
4454 VOID\r
4455 LoadGameDialog(HWND hwnd, char* title)\r
4456 {\r
4457   UINT number = 0;\r
4458   FILE *f;\r
4459   char fileTitle[MSG_SIZ];\r
4460   f = OpenFileDialog(hwnd, "rb", "",\r
4461                      appData.oldSaveStyle ? "gam" : "pgn",\r
4462                      GAME_FILT,\r
4463                      title, &number, fileTitle, NULL);\r
4464   if (f != NULL) {\r
4465     cmailMsgLoaded = FALSE;\r
4466     if (number == 0) {\r
4467       int error = GameListBuild(f);\r
4468       if (error) {\r
4469         DisplayError(_("Cannot build game list"), error);\r
4470       } else if (!ListEmpty(&gameList) &&\r
4471                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4472         GameListPopUp(f, fileTitle);\r
4473         return;\r
4474       }\r
4475       GameListDestroy();\r
4476       number = 1;\r
4477     }\r
4478     LoadGame(f, number, fileTitle, FALSE);\r
4479   }\r
4480 }\r
4481 \r
4482 int get_term_width()\r
4483 {\r
4484     HDC hdc;\r
4485     TEXTMETRIC tm;\r
4486     RECT rc;\r
4487     HFONT hfont, hold_font;\r
4488     LOGFONT lf;\r
4489     HWND hText;\r
4490 \r
4491     if (hwndConsole)\r
4492         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4493     else\r
4494         return 79;\r
4495 \r
4496     // get the text metrics\r
4497     hdc = GetDC(hText);\r
4498     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4499     if (consoleCF.dwEffects & CFE_BOLD)\r
4500         lf.lfWeight = FW_BOLD;\r
4501     if (consoleCF.dwEffects & CFE_ITALIC)\r
4502         lf.lfItalic = TRUE;\r
4503     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4504         lf.lfStrikeOut = TRUE;\r
4505     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4506         lf.lfUnderline = TRUE;\r
4507     hfont = CreateFontIndirect(&lf);\r
4508     hold_font = SelectObject(hdc, hfont);\r
4509     GetTextMetrics(hdc, &tm);\r
4510     SelectObject(hdc, hold_font);\r
4511     DeleteObject(hfont);\r
4512     ReleaseDC(hText, hdc);\r
4513 \r
4514     // get the rectangle\r
4515     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4516 \r
4517     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4518 }\r
4519 \r
4520 void UpdateICSWidth(HWND hText)\r
4521 {\r
4522     LONG old_width, new_width;\r
4523 \r
4524     new_width = get_term_width(hText, FALSE);\r
4525     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4526     if (new_width != old_width)\r
4527     {\r
4528         ics_update_width(new_width);\r
4529         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4530     }\r
4531 }\r
4532 \r
4533 VOID\r
4534 ChangedConsoleFont()\r
4535 {\r
4536   CHARFORMAT cfmt;\r
4537   CHARRANGE tmpsel, sel;\r
4538   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4539   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4540   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4541   PARAFORMAT paraf;\r
4542 \r
4543   cfmt.cbSize = sizeof(CHARFORMAT);\r
4544   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4545     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4546                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4547   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4548    * size.  This was undocumented in the version of MSVC++ that I had\r
4549    * when I wrote the code, but is apparently documented now.\r
4550    */\r
4551   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4552   cfmt.bCharSet = f->lf.lfCharSet;\r
4553   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4554   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4555   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4556   /* Why are the following seemingly needed too? */\r
4557   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4558   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4559   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4560   tmpsel.cpMin = 0;\r
4561   tmpsel.cpMax = -1; /*999999?*/\r
4562   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4563   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4564   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4565    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4566    */\r
4567   paraf.cbSize = sizeof(paraf);\r
4568   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4569   paraf.dxStartIndent = 0;\r
4570   paraf.dxOffset = WRAP_INDENT;\r
4571   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4572   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4573   UpdateICSWidth(hText);\r
4574 }\r
4575 \r
4576 /*---------------------------------------------------------------------------*\\r
4577  *\r
4578  * Window Proc for main window\r
4579  *\r
4580 \*---------------------------------------------------------------------------*/\r
4581 \r
4582 /* Process messages for main window, etc. */\r
4583 LRESULT CALLBACK\r
4584 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4585 {\r
4586   FARPROC lpProc;\r
4587   int wmId, wmEvent;\r
4588   char *defName;\r
4589   FILE *f;\r
4590   UINT number;\r
4591   char fileTitle[MSG_SIZ];\r
4592   char buf[MSG_SIZ];\r
4593   static SnapData sd;\r
4594   static int peek=0;\r
4595 \r
4596   switch (message) {\r
4597 \r
4598   case WM_PAINT: /* message: repaint portion of window */\r
4599     PaintProc(hwnd);\r
4600     break;\r
4601 \r
4602   case WM_ERASEBKGND:\r
4603     if (IsIconic(hwnd)) {\r
4604       /* Cheat; change the message */\r
4605       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4606     } else {\r
4607       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4608     }\r
4609     break;\r
4610 \r
4611   case WM_LBUTTONDOWN:\r
4612   case WM_MBUTTONDOWN:\r
4613   case WM_RBUTTONDOWN:\r
4614   case WM_LBUTTONUP:\r
4615   case WM_MBUTTONUP:\r
4616   case WM_RBUTTONUP:\r
4617   case WM_MOUSEMOVE:\r
4618   case WM_MOUSEWHEEL:\r
4619     MouseEvent(hwnd, message, wParam, lParam);\r
4620     break;\r
4621 \r
4622   case WM_KEYUP:\r
4623     if((char)wParam == '\b') {\r
4624       ForwardEvent(); peek = 0;\r
4625     }\r
4626 \r
4627     JAWS_KBUP_NAVIGATION\r
4628 \r
4629     break;\r
4630 \r
4631   case WM_KEYDOWN:\r
4632     if((char)wParam == '\b') {\r
4633       if(!peek) BackwardEvent(), peek = 1;\r
4634     }\r
4635 \r
4636     JAWS_KBDOWN_NAVIGATION\r
4637 \r
4638     break;\r
4639 \r
4640   case WM_CHAR:\r
4641     \r
4642     JAWS_ALT_INTERCEPT\r
4643 \r
4644     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4645         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4646         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4647         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4648         SetFocus(h);\r
4649         SendMessage(h, message, wParam, lParam);\r
4650     } else if(lParam != KF_REPEAT) {\r
4651         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4652                 TypeInEvent((char)wParam);\r
4653         } else if((char)wParam == 003) CopyGameToClipboard();\r
4654          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4655     }\r
4656 \r
4657     break;\r
4658 \r
4659   case WM_PALETTECHANGED:\r
4660     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4661       int nnew;\r
4662       HDC hdc = GetDC(hwndMain);\r
4663       SelectPalette(hdc, hPal, TRUE);\r
4664       nnew = RealizePalette(hdc);\r
4665       if (nnew > 0) {\r
4666         paletteChanged = TRUE;\r
4667         InvalidateRect(hwnd, &boardRect, FALSE);\r
4668       }\r
4669       ReleaseDC(hwnd, hdc);\r
4670     }\r
4671     break;\r
4672 \r
4673   case WM_QUERYNEWPALETTE:\r
4674     if (!appData.monoMode /*&& paletteChanged*/) {\r
4675       int nnew;\r
4676       HDC hdc = GetDC(hwndMain);\r
4677       paletteChanged = FALSE;\r
4678       SelectPalette(hdc, hPal, FALSE);\r
4679       nnew = RealizePalette(hdc);\r
4680       if (nnew > 0) {\r
4681         InvalidateRect(hwnd, &boardRect, FALSE);\r
4682       }\r
4683       ReleaseDC(hwnd, hdc);\r
4684       return TRUE;\r
4685     }\r
4686     return FALSE;\r
4687 \r
4688   case WM_COMMAND: /* message: command from application menu */\r
4689     wmId    = LOWORD(wParam);\r
4690     wmEvent = HIWORD(wParam);\r
4691 \r
4692     switch (wmId) {\r
4693     case IDM_NewGame:\r
4694       ResetGameEvent();\r
4695       SAY("new game enter a move to play against the computer with white");\r
4696       break;\r
4697 \r
4698     case IDM_NewGameFRC:\r
4699       if( NewGameFRC() == 0 ) {\r
4700         ResetGameEvent();\r
4701       }\r
4702       break;\r
4703 \r
4704     case IDM_NewVariant:\r
4705       NewVariantPopup(hwnd);\r
4706       break;\r
4707 \r
4708     case IDM_LoadGame:\r
4709       LoadGameDialog(hwnd, _("Load Game from File"));\r
4710       break;\r
4711 \r
4712     case IDM_LoadNextGame:\r
4713       ReloadGame(1);\r
4714       break;\r
4715 \r
4716     case IDM_LoadPrevGame:\r
4717       ReloadGame(-1);\r
4718       break;\r
4719 \r
4720     case IDM_ReloadGame:\r
4721       ReloadGame(0);\r
4722       break;\r
4723 \r
4724     case IDM_LoadPosition:\r
4725       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4726         Reset(FALSE, TRUE);\r
4727       }\r
4728       number = 1;\r
4729       f = OpenFileDialog(hwnd, "rb", "",\r
4730                          appData.oldSaveStyle ? "pos" : "fen",\r
4731                          POSITION_FILT,\r
4732                          _("Load Position from File"), &number, fileTitle, NULL);\r
4733       if (f != NULL) {\r
4734         LoadPosition(f, number, fileTitle);\r
4735       }\r
4736       break;\r
4737 \r
4738     case IDM_LoadNextPosition:\r
4739       ReloadPosition(1);\r
4740       break;\r
4741 \r
4742     case IDM_LoadPrevPosition:\r
4743       ReloadPosition(-1);\r
4744       break;\r
4745 \r
4746     case IDM_ReloadPosition:\r
4747       ReloadPosition(0);\r
4748       break;\r
4749 \r
4750     case IDM_SaveGame:\r
4751       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4752       f = OpenFileDialog(hwnd, "a", defName,\r
4753                          appData.oldSaveStyle ? "gam" : "pgn",\r
4754                          GAME_FILT,\r
4755                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4756       if (f != NULL) {\r
4757         SaveGame(f, 0, "");\r
4758       }\r
4759       break;\r
4760 \r
4761     case IDM_SavePosition:\r
4762       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4763       f = OpenFileDialog(hwnd, "a", defName,\r
4764                          appData.oldSaveStyle ? "pos" : "fen",\r
4765                          POSITION_FILT,\r
4766                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4767       if (f != NULL) {\r
4768         SavePosition(f, 0, "");\r
4769       }\r
4770       break;\r
4771 \r
4772     case IDM_SaveDiagram:\r
4773       defName = "diagram";\r
4774       f = OpenFileDialog(hwnd, "wb", defName,\r
4775                          "bmp",\r
4776                          DIAGRAM_FILT,\r
4777                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4778       if (f != NULL) {\r
4779         SaveDiagram(f);\r
4780       }\r
4781       break;\r
4782 \r
4783     case IDM_CopyGame:\r
4784       CopyGameToClipboard();\r
4785       break;\r
4786 \r
4787     case IDM_PasteGame:\r
4788       PasteGameFromClipboard();\r
4789       break;\r
4790 \r
4791     case IDM_CopyGameListToClipboard:\r
4792       CopyGameListToClipboard();\r
4793       break;\r
4794 \r
4795     /* [AS] Autodetect FEN or PGN data */\r
4796     case IDM_PasteAny:\r
4797       PasteGameOrFENFromClipboard();\r
4798       break;\r
4799 \r
4800     /* [AS] Move history */\r
4801     case IDM_ShowMoveHistory:\r
4802         if( MoveHistoryIsUp() ) {\r
4803             MoveHistoryPopDown();\r
4804         }\r
4805         else {\r
4806             MoveHistoryPopUp();\r
4807         }\r
4808         break;\r
4809 \r
4810     /* [AS] Eval graph */\r
4811     case IDM_ShowEvalGraph:\r
4812         if( EvalGraphIsUp() ) {\r
4813             EvalGraphPopDown();\r
4814         }\r
4815         else {\r
4816             EvalGraphPopUp();\r
4817             SetFocus(hwndMain);\r
4818         }\r
4819         break;\r
4820 \r
4821     /* [AS] Engine output */\r
4822     case IDM_ShowEngineOutput:\r
4823         if( EngineOutputIsUp() ) {\r
4824             EngineOutputPopDown();\r
4825         }\r
4826         else {\r
4827             EngineOutputPopUp();\r
4828         }\r
4829         break;\r
4830 \r
4831     /* [AS] User adjudication */\r
4832     case IDM_UserAdjudication_White:\r
4833         UserAdjudicationEvent( +1 );\r
4834         break;\r
4835 \r
4836     case IDM_UserAdjudication_Black:\r
4837         UserAdjudicationEvent( -1 );\r
4838         break;\r
4839 \r
4840     case IDM_UserAdjudication_Draw:\r
4841         UserAdjudicationEvent( 0 );\r
4842         break;\r
4843 \r
4844     /* [AS] Game list options dialog */\r
4845     case IDM_GameListOptions:\r
4846       GameListOptions();\r
4847       break;\r
4848 \r
4849     case IDM_NewChat:\r
4850       ChatPopUp(NULL);\r
4851       break;\r
4852 \r
4853     case IDM_CopyPosition:\r
4854       CopyFENToClipboard();\r
4855       break;\r
4856 \r
4857     case IDM_PastePosition:\r
4858       PasteFENFromClipboard();\r
4859       break;\r
4860 \r
4861     case IDM_MailMove:\r
4862       MailMoveEvent();\r
4863       break;\r
4864 \r
4865     case IDM_ReloadCMailMsg:\r
4866       Reset(TRUE, TRUE);\r
4867       ReloadCmailMsgEvent(FALSE);\r
4868       break;\r
4869 \r
4870     case IDM_Minimize:\r
4871       ShowWindow(hwnd, SW_MINIMIZE);\r
4872       break;\r
4873 \r
4874     case IDM_Exit:\r
4875       ExitEvent(0);\r
4876       break;\r
4877 \r
4878     case IDM_MachineWhite:\r
4879       MachineWhiteEvent();\r
4880       /*\r
4881        * refresh the tags dialog only if it's visible\r
4882        */\r
4883       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4884           char *tags;\r
4885           tags = PGNTags(&gameInfo);\r
4886           TagsPopUp(tags, CmailMsg());\r
4887           free(tags);\r
4888       }\r
4889       SAY("computer starts playing white");\r
4890       break;\r
4891 \r
4892     case IDM_MachineBlack:\r
4893       MachineBlackEvent();\r
4894       /*\r
4895        * refresh the tags dialog only if it's visible\r
4896        */\r
4897       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4898           char *tags;\r
4899           tags = PGNTags(&gameInfo);\r
4900           TagsPopUp(tags, CmailMsg());\r
4901           free(tags);\r
4902       }\r
4903       SAY("computer starts playing black");\r
4904       break;\r
4905 \r
4906     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4907       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4908       break;\r
4909 \r
4910     case IDM_TwoMachines:\r
4911       TwoMachinesEvent();\r
4912       /*\r
4913        * refresh the tags dialog only if it's visible\r
4914        */\r
4915       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4916           char *tags;\r
4917           tags = PGNTags(&gameInfo);\r
4918           TagsPopUp(tags, CmailMsg());\r
4919           free(tags);\r
4920       }\r
4921       SAY("computer starts playing both sides");\r
4922       break;\r
4923 \r
4924     case IDM_AnalysisMode:\r
4925       if (!first.analysisSupport) {\r
4926         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4927         DisplayError(buf, 0);\r
4928       } else {\r
4929         SAY("analyzing current position");\r
4930         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4931         if (appData.icsActive) {\r
4932                if (gameMode != IcsObserving) {\r
4933                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4934                        DisplayError(buf, 0);\r
4935                        /* secure check */\r
4936                        if (appData.icsEngineAnalyze) {\r
4937                                if (appData.debugMode) \r
4938                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4939                                ExitAnalyzeMode();\r
4940                                ModeHighlight();\r
4941                                break;\r
4942                        }\r
4943                        break;\r
4944                } else {\r
4945                        /* if enable, user want disable icsEngineAnalyze */\r
4946                        if (appData.icsEngineAnalyze) {\r
4947                                ExitAnalyzeMode();\r
4948                                ModeHighlight();\r
4949                                break;\r
4950                        }\r
4951                        appData.icsEngineAnalyze = TRUE;\r
4952                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4953                }\r
4954         } \r
4955         if (!appData.showThinking) ToggleShowThinking();\r
4956         AnalyzeModeEvent();\r
4957       }\r
4958       break;\r
4959 \r
4960     case IDM_AnalyzeFile:\r
4961       if (!first.analysisSupport) {\r
4962         char buf[MSG_SIZ];\r
4963           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4964         DisplayError(buf, 0);\r
4965       } else {\r
4966         if (!appData.showThinking) ToggleShowThinking();\r
4967         AnalyzeFileEvent();\r
4968 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4969         AnalysisPeriodicEvent(1);\r
4970       }\r
4971       break;\r
4972 \r
4973     case IDM_IcsClient:\r
4974       IcsClientEvent();\r
4975       break;\r
4976 \r
4977     case IDM_EditGame:\r
4978     case IDM_EditGame2:\r
4979       EditGameEvent();\r
4980       SAY("edit game");\r
4981       break;\r
4982 \r
4983     case IDM_EditPosition:\r
4984     case IDM_EditPosition2:\r
4985       EditPositionEvent();\r
4986       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4987       break;\r
4988 \r
4989     case IDM_Training:\r
4990       TrainingEvent();\r
4991       break;\r
4992 \r
4993     case IDM_ShowGameList:\r
4994       ShowGameListProc();\r
4995       break;\r
4996 \r
4997     case IDM_EditProgs1:\r
4998       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4999       break;\r
5000 \r
5001     case IDM_LoadProg1:\r
5002      LoadEnginePopUp(hwndMain, 0);\r
5003       break;\r
5004 \r
5005     case IDM_LoadProg2:\r
5006      LoadEnginePopUp(hwndMain, 1);\r
5007       break;\r
5008 \r
5009     case IDM_EditServers:\r
5010       EditTagsPopUp(icsNames, &icsNames);\r
5011       break;\r
5012 \r
5013     case IDM_EditTags:\r
5014     case IDM_Tags:\r
5015       EditTagsProc();\r
5016       break;\r
5017 \r
5018     case IDM_EditBook:\r
5019       EditBookEvent();\r
5020       break;\r
5021 \r
5022     case IDM_EditComment:\r
5023     case IDM_Comment:\r
5024       if (commentUp && editComment) {\r
5025         CommentPopDown();\r
5026       } else {\r
5027         EditCommentEvent();\r
5028       }\r
5029       break;\r
5030 \r
5031     case IDM_Pause:\r
5032       PauseEvent();\r
5033       break;\r
5034 \r
5035     case IDM_Accept:\r
5036       AcceptEvent();\r
5037       break;\r
5038 \r
5039     case IDM_Decline:\r
5040       DeclineEvent();\r
5041       break;\r
5042 \r
5043     case IDM_Rematch:\r
5044       RematchEvent();\r
5045       break;\r
5046 \r
5047     case IDM_CallFlag:\r
5048       CallFlagEvent();\r
5049       break;\r
5050 \r
5051     case IDM_Draw:\r
5052       DrawEvent();\r
5053       break;\r
5054 \r
5055     case IDM_Adjourn:\r
5056       AdjournEvent();\r
5057       break;\r
5058 \r
5059     case IDM_Abort:\r
5060       AbortEvent();\r
5061       break;\r
5062 \r
5063     case IDM_Resign:\r
5064       ResignEvent();\r
5065       break;\r
5066 \r
5067     case IDM_StopObserving:\r
5068       StopObservingEvent();\r
5069       break;\r
5070 \r
5071     case IDM_StopExamining:\r
5072       StopExaminingEvent();\r
5073       break;\r
5074 \r
5075     case IDM_Upload:\r
5076       UploadGameEvent();\r
5077       break;\r
5078 \r
5079     case IDM_TypeInMove:\r
5080       TypeInEvent('\000');\r
5081       break;\r
5082 \r
5083     case IDM_TypeInName:\r
5084       PopUpNameDialog('\000');\r
5085       break;\r
5086 \r
5087     case IDM_Backward:\r
5088       BackwardEvent();\r
5089       SetFocus(hwndMain);\r
5090       break;\r
5091 \r
5092     JAWS_MENU_ITEMS\r
5093 \r
5094     case IDM_Forward:\r
5095       ForwardEvent();\r
5096       SetFocus(hwndMain);\r
5097       break;\r
5098 \r
5099     case IDM_ToStart:\r
5100       ToStartEvent();\r
5101       SetFocus(hwndMain);\r
5102       break;\r
5103 \r
5104     case IDM_ToEnd:\r
5105       ToEndEvent();\r
5106       SetFocus(hwndMain);\r
5107       break;\r
5108 \r
5109     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5110     case OPT_GameListPrev:\r
5111       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5112       break;\r
5113 \r
5114     case IDM_Revert:\r
5115       RevertEvent(FALSE);\r
5116       break;\r
5117 \r
5118     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5119       RevertEvent(TRUE);\r
5120       break;\r
5121 \r
5122     case IDM_TruncateGame:\r
5123       TruncateGameEvent();\r
5124       break;\r
5125 \r
5126     case IDM_MoveNow:\r
5127       MoveNowEvent();\r
5128       break;\r
5129 \r
5130     case IDM_RetractMove:\r
5131       RetractMoveEvent();\r
5132       break;\r
5133 \r
5134     case IDM_FlipView:\r
5135       flipView = !flipView;\r
5136       DrawPosition(FALSE, NULL);\r
5137       break;\r
5138 \r
5139     case IDM_FlipClock:\r
5140       flipClock = !flipClock;\r
5141       DisplayBothClocks();\r
5142       DisplayLogos();\r
5143       break;\r
5144 \r
5145     case IDM_MuteSounds:\r
5146       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5147       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5148                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5149       break;\r
5150 \r
5151     case IDM_GeneralOptions:\r
5152       GeneralOptionsPopup(hwnd);\r
5153       DrawPosition(TRUE, NULL);\r
5154       break;\r
5155 \r
5156     case IDM_BoardOptions:\r
5157       BoardOptionsPopup(hwnd);\r
5158       break;\r
5159 \r
5160     case IDM_EnginePlayOptions:\r
5161       EnginePlayOptionsPopup(hwnd);\r
5162       break;\r
5163 \r
5164     case IDM_Engine1Options:\r
5165       EngineOptionsPopup(hwnd, &first);\r
5166       break;\r
5167 \r
5168     case IDM_Engine2Options:\r
5169       savedHwnd = hwnd;\r
5170       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5171       EngineOptionsPopup(hwnd, &second);\r
5172       break;\r
5173 \r
5174     case IDM_OptionsUCI:\r
5175       UciOptionsPopup(hwnd);\r
5176       break;\r
5177 \r
5178     case IDM_Tourney:\r
5179       TourneyPopup(hwnd);\r
5180       break;\r
5181 \r
5182     case IDM_IcsOptions:\r
5183       IcsOptionsPopup(hwnd);\r
5184       break;\r
5185 \r
5186     case IDM_Fonts:\r
5187       FontsOptionsPopup(hwnd);\r
5188       break;\r
5189 \r
5190     case IDM_Sounds:\r
5191       SoundOptionsPopup(hwnd);\r
5192       break;\r
5193 \r
5194     case IDM_CommPort:\r
5195       CommPortOptionsPopup(hwnd);\r
5196       break;\r
5197 \r
5198     case IDM_LoadOptions:\r
5199       LoadOptionsPopup(hwnd);\r
5200       break;\r
5201 \r
5202     case IDM_SaveOptions:\r
5203       SaveOptionsPopup(hwnd);\r
5204       break;\r
5205 \r
5206     case IDM_TimeControl:\r
5207       TimeControlOptionsPopup(hwnd);\r
5208       break;\r
5209 \r
5210     case IDM_SaveSettings:\r
5211       SaveSettings(settingsFileName);\r
5212       break;\r
5213 \r
5214     case IDM_SaveSettingsOnExit:\r
5215       saveSettingsOnExit = !saveSettingsOnExit;\r
5216       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5217                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5218                                          MF_CHECKED : MF_UNCHECKED));\r
5219       break;\r
5220 \r
5221     case IDM_Hint:\r
5222       HintEvent();\r
5223       break;\r
5224 \r
5225     case IDM_Book:\r
5226       BookEvent();\r
5227       break;\r
5228 \r
5229     case IDM_AboutGame:\r
5230       AboutGameEvent();\r
5231       break;\r
5232 \r
5233     case IDM_Debug:\r
5234       appData.debugMode = !appData.debugMode;\r
5235       if (appData.debugMode) {\r
5236         char dir[MSG_SIZ];\r
5237         GetCurrentDirectory(MSG_SIZ, dir);\r
5238         SetCurrentDirectory(installDir);\r
5239         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5240         SetCurrentDirectory(dir);\r
5241         setbuf(debugFP, NULL);\r
5242       } else {\r
5243         fclose(debugFP);\r
5244         debugFP = NULL;\r
5245       }\r
5246       break;\r
5247 \r
5248     case IDM_HELPCONTENTS:\r
5249       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5250           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5251           MessageBox (GetFocus(),\r
5252                     _("Unable to activate help"),\r
5253                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5254       }\r
5255       break;\r
5256 \r
5257     case IDM_HELPSEARCH:\r
5258         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5259             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5260         MessageBox (GetFocus(),\r
5261                     _("Unable to activate help"),\r
5262                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5263       }\r
5264       break;\r
5265 \r
5266     case IDM_HELPHELP:\r
5267       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5268         MessageBox (GetFocus(),\r
5269                     _("Unable to activate help"),\r
5270                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5271       }\r
5272       break;\r
5273 \r
5274     case IDM_ABOUT:\r
5275       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5276       DialogBox(hInst, \r
5277         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5278         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5279       FreeProcInstance(lpProc);\r
5280       break;\r
5281 \r
5282     case IDM_DirectCommand1:\r
5283       AskQuestionEvent(_("Direct Command"),\r
5284                        _("Send to chess program:"), "", "1");\r
5285       break;\r
5286     case IDM_DirectCommand2:\r
5287       AskQuestionEvent(_("Direct Command"),\r
5288                        _("Send to second chess program:"), "", "2");\r
5289       break;\r
5290 \r
5291     case EP_WhitePawn:\r
5292       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_WhiteKnight:\r
5297       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_WhiteBishop:\r
5302       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_WhiteRook:\r
5307       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_WhiteQueen:\r
5312       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_WhiteFerz:\r
5317       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_WhiteWazir:\r
5322       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_WhiteAlfil:\r
5327       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_WhiteCannon:\r
5332       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_WhiteCardinal:\r
5337       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_WhiteMarshall:\r
5342       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_WhiteKing:\r
5347       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_BlackPawn:\r
5352       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_BlackKnight:\r
5357       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_BlackBishop:\r
5362       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_BlackRook:\r
5367       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_BlackQueen:\r
5372       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_BlackFerz:\r
5377       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_BlackWazir:\r
5382       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_BlackAlfil:\r
5387       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_BlackCannon:\r
5392       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_BlackCardinal:\r
5397       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_BlackMarshall:\r
5402       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_BlackKing:\r
5407       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_EmptySquare:\r
5412       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_ClearBoard:\r
5417       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_White:\r
5422       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_Black:\r
5427       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_Promote:\r
5432       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case EP_Demote:\r
5437       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case DP_Pawn:\r
5442       DropMenuEvent(WhitePawn, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case DP_Knight:\r
5447       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case DP_Bishop:\r
5452       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case DP_Rook:\r
5457       DropMenuEvent(WhiteRook, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case DP_Queen:\r
5462       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5463       fromX = fromY = -1;\r
5464       break;\r
5465 \r
5466     case IDM_English:\r
5467       barbaric = 0; appData.language = "";\r
5468       TranslateMenus(0);\r
5469       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5470       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5471       lastChecked = wmId;\r
5472       break;\r
5473 \r
5474     default:\r
5475       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5476           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5477       else\r
5478       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5479           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5480           TranslateMenus(0);\r
5481           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5482           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5483           lastChecked = wmId;\r
5484           break;\r
5485       }\r
5486       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5487     }\r
5488     break;\r
5489 \r
5490   case WM_TIMER:\r
5491     switch (wParam) {\r
5492     case CLOCK_TIMER_ID:\r
5493       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5494       clockTimerEvent = 0;\r
5495       DecrementClocks(); /* call into back end */\r
5496       break;\r
5497     case LOAD_GAME_TIMER_ID:\r
5498       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5499       loadGameTimerEvent = 0;\r
5500       AutoPlayGameLoop(); /* call into back end */\r
5501       break;\r
5502     case ANALYSIS_TIMER_ID:\r
5503       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5504                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5505         AnalysisPeriodicEvent(0);\r
5506       } else {\r
5507         KillTimer(hwnd, analysisTimerEvent);\r
5508         analysisTimerEvent = 0;\r
5509       }\r
5510       break;\r
5511     case DELAYED_TIMER_ID:\r
5512       KillTimer(hwnd, delayedTimerEvent);\r
5513       delayedTimerEvent = 0;\r
5514       delayedTimerCallback();\r
5515       break;\r
5516     }\r
5517     break;\r
5518 \r
5519   case WM_USER_Input:\r
5520     InputEvent(hwnd, message, wParam, lParam);\r
5521     break;\r
5522 \r
5523   /* [AS] Also move "attached" child windows */\r
5524   case WM_WINDOWPOSCHANGING:\r
5525 \r
5526     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5527         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5528 \r
5529         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5530             /* Window is moving */\r
5531             RECT rcMain;\r
5532 \r
5533 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5534             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5535             rcMain.right  = wpMain.x + wpMain.width;\r
5536             rcMain.top    = wpMain.y;\r
5537             rcMain.bottom = wpMain.y + wpMain.height;\r
5538             \r
5539             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5540             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5541             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5542             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5543             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5544             wpMain.x = lpwp->x;\r
5545             wpMain.y = lpwp->y;\r
5546         }\r
5547     }\r
5548     break;\r
5549 \r
5550   /* [AS] Snapping */\r
5551   case WM_ENTERSIZEMOVE:\r
5552     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5553     if (hwnd == hwndMain) {\r
5554       doingSizing = TRUE;\r
5555       lastSizing = 0;\r
5556     }\r
5557     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5558     break;\r
5559 \r
5560   case WM_SIZING:\r
5561     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5562     if (hwnd == hwndMain) {\r
5563       lastSizing = wParam;\r
5564     }\r
5565     break;\r
5566 \r
5567   case WM_MOVING:\r
5568     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5569       return OnMoving( &sd, hwnd, wParam, lParam );\r
5570 \r
5571   case WM_EXITSIZEMOVE:\r
5572     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5573     if (hwnd == hwndMain) {\r
5574       RECT client;\r
5575       doingSizing = FALSE;\r
5576       InvalidateRect(hwnd, &boardRect, FALSE);\r
5577       GetClientRect(hwnd, &client);\r
5578       ResizeBoard(client.right, client.bottom, lastSizing);\r
5579       lastSizing = 0;\r
5580       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5581     }\r
5582     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5583     break;\r
5584 \r
5585   case WM_DESTROY: /* message: window being destroyed */\r
5586     PostQuitMessage(0);\r
5587     break;\r
5588 \r
5589   case WM_CLOSE:\r
5590     if (hwnd == hwndMain) {\r
5591       ExitEvent(0);\r
5592     }\r
5593     break;\r
5594 \r
5595   default:      /* Passes it on if unprocessed */\r
5596     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5597   }\r
5598   return 0;\r
5599 }\r
5600 \r
5601 /*---------------------------------------------------------------------------*\\r
5602  *\r
5603  * Misc utility routines\r
5604  *\r
5605 \*---------------------------------------------------------------------------*/\r
5606 \r
5607 /*\r
5608  * Decent random number generator, at least not as bad as Windows\r
5609  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5610  */\r
5611 unsigned int randstate;\r
5612 \r
5613 int\r
5614 myrandom(void)\r
5615 {\r
5616   randstate = randstate * 1664525 + 1013904223;\r
5617   return (int) randstate & 0x7fffffff;\r
5618 }\r
5619 \r
5620 void\r
5621 mysrandom(unsigned int seed)\r
5622 {\r
5623   randstate = seed;\r
5624 }\r
5625 \r
5626 \r
5627 /* \r
5628  * returns TRUE if user selects a different color, FALSE otherwise \r
5629  */\r
5630 \r
5631 BOOL\r
5632 ChangeColor(HWND hwnd, COLORREF *which)\r
5633 {\r
5634   static BOOL firstTime = TRUE;\r
5635   static DWORD customColors[16];\r
5636   CHOOSECOLOR cc;\r
5637   COLORREF newcolor;\r
5638   int i;\r
5639   ColorClass ccl;\r
5640 \r
5641   if (firstTime) {\r
5642     /* Make initial colors in use available as custom colors */\r
5643     /* Should we put the compiled-in defaults here instead? */\r
5644     i = 0;\r
5645     customColors[i++] = lightSquareColor & 0xffffff;\r
5646     customColors[i++] = darkSquareColor & 0xffffff;\r
5647     customColors[i++] = whitePieceColor & 0xffffff;\r
5648     customColors[i++] = blackPieceColor & 0xffffff;\r
5649     customColors[i++] = highlightSquareColor & 0xffffff;\r
5650     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5651 \r
5652     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5653       customColors[i++] = textAttribs[ccl].color;\r
5654     }\r
5655     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5656     firstTime = FALSE;\r
5657   }\r
5658 \r
5659   cc.lStructSize = sizeof(cc);\r
5660   cc.hwndOwner = hwnd;\r
5661   cc.hInstance = NULL;\r
5662   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5663   cc.lpCustColors = (LPDWORD) customColors;\r
5664   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5665 \r
5666   if (!ChooseColor(&cc)) return FALSE;\r
5667 \r
5668   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5669   if (newcolor == *which) return FALSE;\r
5670   *which = newcolor;\r
5671   return TRUE;\r
5672 \r
5673   /*\r
5674   InitDrawingColors();\r
5675   InvalidateRect(hwnd, &boardRect, FALSE);\r
5676   */\r
5677 }\r
5678 \r
5679 BOOLEAN\r
5680 MyLoadSound(MySound *ms)\r
5681 {\r
5682   BOOL ok = FALSE;\r
5683   struct stat st;\r
5684   FILE *f;\r
5685 \r
5686   if (ms->data && ms->flag) free(ms->data);\r
5687   ms->data = NULL;\r
5688 \r
5689   switch (ms->name[0]) {\r
5690   case NULLCHAR:\r
5691     /* Silence */\r
5692     ok = TRUE;\r
5693     break;\r
5694   case '$':\r
5695     /* System sound from Control Panel.  Don't preload here. */\r
5696     ok = TRUE;\r
5697     break;\r
5698   case '!':\r
5699     if (ms->name[1] == NULLCHAR) {\r
5700       /* "!" alone = silence */\r
5701       ok = TRUE;\r
5702     } else {\r
5703       /* Builtin wave resource.  Error if not found. */\r
5704       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5705       if (h == NULL) break;\r
5706       ms->data = (void *)LoadResource(hInst, h);\r
5707       ms->flag = 0; // not maloced, so cannot be freed!\r
5708       if (h == NULL) break;\r
5709       ok = TRUE;\r
5710     }\r
5711     break;\r
5712   default:\r
5713     /* .wav file.  Error if not found. */\r
5714     f = fopen(ms->name, "rb");\r
5715     if (f == NULL) break;\r
5716     if (fstat(fileno(f), &st) < 0) break;\r
5717     ms->data = malloc(st.st_size);\r
5718     ms->flag = 1;\r
5719     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5720     fclose(f);\r
5721     ok = TRUE;\r
5722     break;\r
5723   }\r
5724   if (!ok) {\r
5725     char buf[MSG_SIZ];\r
5726       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5727     DisplayError(buf, GetLastError());\r
5728   }\r
5729   return ok;\r
5730 }\r
5731 \r
5732 BOOLEAN\r
5733 MyPlaySound(MySound *ms)\r
5734 {\r
5735   BOOLEAN ok = FALSE;\r
5736 \r
5737   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5738   switch (ms->name[0]) {\r
5739   case NULLCHAR:\r
5740         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5741     /* Silence */\r
5742     ok = TRUE;\r
5743     break;\r
5744   case '$':\r
5745     /* System sound from Control Panel (deprecated feature).\r
5746        "$" alone or an unset sound name gets default beep (still in use). */\r
5747     if (ms->name[1]) {\r
5748       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5749     }\r
5750     if (!ok) ok = MessageBeep(MB_OK);\r
5751     break; \r
5752   case '!':\r
5753     /* Builtin wave resource, or "!" alone for silence */\r
5754     if (ms->name[1]) {\r
5755       if (ms->data == NULL) return FALSE;\r
5756       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5757     } else {\r
5758       ok = TRUE;\r
5759     }\r
5760     break;\r
5761   default:\r
5762     /* .wav file.  Error if not found. */\r
5763     if (ms->data == NULL) return FALSE;\r
5764     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5765     break;\r
5766   }\r
5767   /* Don't print an error: this can happen innocently if the sound driver\r
5768      is busy; for instance, if another instance of WinBoard is playing\r
5769      a sound at about the same time. */\r
5770   return ok;\r
5771 }\r
5772 \r
5773 \r
5774 LRESULT CALLBACK\r
5775 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5776 {\r
5777   BOOL ok;\r
5778   OPENFILENAME *ofn;\r
5779   static UINT *number; /* gross that this is static */\r
5780 \r
5781   switch (message) {\r
5782   case WM_INITDIALOG: /* message: initialize dialog box */\r
5783     /* Center the dialog over the application window */\r
5784     ofn = (OPENFILENAME *) lParam;\r
5785     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5786       number = (UINT *) ofn->lCustData;\r
5787       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5788     } else {\r
5789       number = NULL;\r
5790     }\r
5791     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5792     Translate(hDlg, 1536);\r
5793     return FALSE;  /* Allow for further processing */\r
5794 \r
5795   case WM_COMMAND:\r
5796     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5797       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5798     }\r
5799     return FALSE;  /* Allow for further processing */\r
5800   }\r
5801   return FALSE;\r
5802 }\r
5803 \r
5804 UINT APIENTRY\r
5805 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5806 {\r
5807   static UINT *number;\r
5808   OPENFILENAME *ofname;\r
5809   OFNOTIFY *ofnot;\r
5810   switch (uiMsg) {\r
5811   case WM_INITDIALOG:\r
5812     Translate(hdlg, DLG_IndexNumber);\r
5813     ofname = (OPENFILENAME *)lParam;\r
5814     number = (UINT *)(ofname->lCustData);\r
5815     break;\r
5816   case WM_NOTIFY:\r
5817     ofnot = (OFNOTIFY *)lParam;\r
5818     if (ofnot->hdr.code == CDN_FILEOK) {\r
5819       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5820     }\r
5821     break;\r
5822   }\r
5823   return 0;\r
5824 }\r
5825 \r
5826 \r
5827 FILE *\r
5828 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5829                char *nameFilt, char *dlgTitle, UINT *number,\r
5830                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5831 {\r
5832   OPENFILENAME openFileName;\r
5833   char buf1[MSG_SIZ];\r
5834   FILE *f;\r
5835 \r
5836   if (fileName == NULL) fileName = buf1;\r
5837   if (defName == NULL) {\r
5838     safeStrCpy(fileName, "*.", 3 );\r
5839     strcat(fileName, defExt);\r
5840   } else {\r
5841     safeStrCpy(fileName, defName, MSG_SIZ );\r
5842   }\r
5843     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5844   if (number) *number = 0;\r
5845 \r
5846   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5847   openFileName.hwndOwner         = hwnd;\r
5848   openFileName.hInstance         = (HANDLE) hInst;\r
5849   openFileName.lpstrFilter       = nameFilt;\r
5850   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5851   openFileName.nMaxCustFilter    = 0L;\r
5852   openFileName.nFilterIndex      = 1L;\r
5853   openFileName.lpstrFile         = fileName;\r
5854   openFileName.nMaxFile          = MSG_SIZ;\r
5855   openFileName.lpstrFileTitle    = fileTitle;\r
5856   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5857   openFileName.lpstrInitialDir   = NULL;\r
5858   openFileName.lpstrTitle        = dlgTitle;\r
5859   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5860     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5861     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5862     | (oldDialog ? 0 : OFN_EXPLORER);\r
5863   openFileName.nFileOffset       = 0;\r
5864   openFileName.nFileExtension    = 0;\r
5865   openFileName.lpstrDefExt       = defExt;\r
5866   openFileName.lCustData         = (LONG) number;\r
5867   openFileName.lpfnHook          = oldDialog ?\r
5868     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5869   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5870 \r
5871   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5872                         GetOpenFileName(&openFileName)) {\r
5873     /* open the file */\r
5874     f = fopen(openFileName.lpstrFile, write);\r
5875     if (f == NULL) {\r
5876       MessageBox(hwnd, _("File open failed"), NULL,\r
5877                  MB_OK|MB_ICONEXCLAMATION);\r
5878       return NULL;\r
5879     }\r
5880   } else {\r
5881     int err = CommDlgExtendedError();\r
5882     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5883     return FALSE;\r
5884   }\r
5885   return f;\r
5886 }\r
5887 \r
5888 \r
5889 \r
5890 VOID APIENTRY\r
5891 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5892 {\r
5893   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5894 \r
5895   /*\r
5896    * Get the first pop-up menu in the menu template. This is the\r
5897    * menu that TrackPopupMenu displays.\r
5898    */\r
5899   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5900   TranslateOneMenu(10, hmenuTrackPopup);\r
5901 \r
5902   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5903 \r
5904   /*\r
5905    * TrackPopup uses screen coordinates, so convert the\r
5906    * coordinates of the mouse click to screen coordinates.\r
5907    */\r
5908   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5909 \r
5910   /* Draw and track the floating pop-up menu. */\r
5911   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5912                  pt.x, pt.y, 0, hwnd, NULL);\r
5913 \r
5914   /* Destroy the menu.*/\r
5915   DestroyMenu(hmenu);\r
5916 }\r
5917    \r
5918 typedef struct {\r
5919   HWND hDlg, hText;\r
5920   int sizeX, sizeY, newSizeX, newSizeY;\r
5921   HDWP hdwp;\r
5922 } ResizeEditPlusButtonsClosure;\r
5923 \r
5924 BOOL CALLBACK\r
5925 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5926 {\r
5927   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5928   RECT rect;\r
5929   POINT pt;\r
5930 \r
5931   if (hChild == cl->hText) return TRUE;\r
5932   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5933   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5934   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5935   ScreenToClient(cl->hDlg, &pt);\r
5936   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5937     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5938   return TRUE;\r
5939 }\r
5940 \r
5941 /* Resize a dialog that has a (rich) edit field filling most of\r
5942    the top, with a row of buttons below */\r
5943 VOID\r
5944 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5945 {\r
5946   RECT rectText;\r
5947   int newTextHeight, newTextWidth;\r
5948   ResizeEditPlusButtonsClosure cl;\r
5949   \r
5950   /*if (IsIconic(hDlg)) return;*/\r
5951   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5952   \r
5953   cl.hdwp = BeginDeferWindowPos(8);\r
5954 \r
5955   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5956   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5957   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5958   if (newTextHeight < 0) {\r
5959     newSizeY += -newTextHeight;\r
5960     newTextHeight = 0;\r
5961   }\r
5962   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5963     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5964 \r
5965   cl.hDlg = hDlg;\r
5966   cl.hText = hText;\r
5967   cl.sizeX = sizeX;\r
5968   cl.sizeY = sizeY;\r
5969   cl.newSizeX = newSizeX;\r
5970   cl.newSizeY = newSizeY;\r
5971   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5972 \r
5973   EndDeferWindowPos(cl.hdwp);\r
5974 }\r
5975 \r
5976 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5977 {\r
5978     RECT    rChild, rParent;\r
5979     int     wChild, hChild, wParent, hParent;\r
5980     int     wScreen, hScreen, xNew, yNew;\r
5981     HDC     hdc;\r
5982 \r
5983     /* Get the Height and Width of the child window */\r
5984     GetWindowRect (hwndChild, &rChild);\r
5985     wChild = rChild.right - rChild.left;\r
5986     hChild = rChild.bottom - rChild.top;\r
5987 \r
5988     /* Get the Height and Width of the parent window */\r
5989     GetWindowRect (hwndParent, &rParent);\r
5990     wParent = rParent.right - rParent.left;\r
5991     hParent = rParent.bottom - rParent.top;\r
5992 \r
5993     /* Get the display limits */\r
5994     hdc = GetDC (hwndChild);\r
5995     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5996     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5997     ReleaseDC(hwndChild, hdc);\r
5998 \r
5999     /* Calculate new X position, then adjust for screen */\r
6000     xNew = rParent.left + ((wParent - wChild) /2);\r
6001     if (xNew < 0) {\r
6002         xNew = 0;\r
6003     } else if ((xNew+wChild) > wScreen) {\r
6004         xNew = wScreen - wChild;\r
6005     }\r
6006 \r
6007     /* Calculate new Y position, then adjust for screen */\r
6008     if( mode == 0 ) {\r
6009         yNew = rParent.top  + ((hParent - hChild) /2);\r
6010     }\r
6011     else {\r
6012         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6013     }\r
6014 \r
6015     if (yNew < 0) {\r
6016         yNew = 0;\r
6017     } else if ((yNew+hChild) > hScreen) {\r
6018         yNew = hScreen - hChild;\r
6019     }\r
6020 \r
6021     /* Set it, and return */\r
6022     return SetWindowPos (hwndChild, NULL,\r
6023                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6024 }\r
6025 \r
6026 /* Center one window over another */\r
6027 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6028 {\r
6029     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6030 }\r
6031 \r
6032 /*---------------------------------------------------------------------------*\\r
6033  *\r
6034  * Startup Dialog functions\r
6035  *\r
6036 \*---------------------------------------------------------------------------*/\r
6037 void\r
6038 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6039 {\r
6040   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6041 \r
6042   while (*cd != NULL) {\r
6043     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6044     cd++;\r
6045   }\r
6046 }\r
6047 \r
6048 void\r
6049 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6050 {\r
6051   char buf1[MAX_ARG_LEN];\r
6052   int len;\r
6053 \r
6054   if (str[0] == '@') {\r
6055     FILE* f = fopen(str + 1, "r");\r
6056     if (f == NULL) {\r
6057       DisplayFatalError(str + 1, errno, 2);\r
6058       return;\r
6059     }\r
6060     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6061     fclose(f);\r
6062     buf1[len] = NULLCHAR;\r
6063     str = buf1;\r
6064   }\r
6065 \r
6066   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6067 \r
6068   for (;;) {\r
6069     char buf[MSG_SIZ];\r
6070     char *end = strchr(str, '\n');\r
6071     if (end == NULL) return;\r
6072     memcpy(buf, str, end - str);\r
6073     buf[end - str] = NULLCHAR;\r
6074     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6075     str = end + 1;\r
6076   }\r
6077 }\r
6078 \r
6079 void\r
6080 SetStartupDialogEnables(HWND hDlg)\r
6081 {\r
6082   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6083     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6084     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6085   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6086     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6087   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6088     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6089   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6090     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6091   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6092     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6093     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6094     IsDlgButtonChecked(hDlg, OPT_View));\r
6095 }\r
6096 \r
6097 char *\r
6098 QuoteForFilename(char *filename)\r
6099 {\r
6100   int dquote, space;\r
6101   dquote = strchr(filename, '"') != NULL;\r
6102   space = strchr(filename, ' ') != NULL;\r
6103   if (dquote || space) {\r
6104     if (dquote) {\r
6105       return "'";\r
6106     } else {\r
6107       return "\"";\r
6108     }\r
6109   } else {\r
6110     return "";\r
6111   }\r
6112 }\r
6113 \r
6114 VOID\r
6115 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6116 {\r
6117   char buf[MSG_SIZ];\r
6118   char *q;\r
6119 \r
6120   InitComboStringsFromOption(hwndCombo, nthnames);\r
6121   q = QuoteForFilename(nthcp);\r
6122     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6123   if (*nthdir != NULLCHAR) {\r
6124     q = QuoteForFilename(nthdir);\r
6125       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6126   }\r
6127   if (*nthcp == NULLCHAR) {\r
6128     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6129   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6130     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6131     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6132   }\r
6133 }\r
6134 \r
6135 LRESULT CALLBACK\r
6136 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6137 {\r
6138   char buf[MSG_SIZ];\r
6139   HANDLE hwndCombo;\r
6140   char *p;\r
6141 \r
6142   switch (message) {\r
6143   case WM_INITDIALOG:\r
6144     /* Center the dialog */\r
6145     CenterWindow (hDlg, GetDesktopWindow());\r
6146     Translate(hDlg, DLG_Startup);\r
6147     /* Initialize the dialog items */\r
6148     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6149                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6150                   firstChessProgramNames);\r
6151     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6152                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6153                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6154     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6155     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6156       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6157     if (*appData.icsHelper != NULLCHAR) {\r
6158       char *q = QuoteForFilename(appData.icsHelper);\r
6159       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6160     }\r
6161     if (*appData.icsHost == NULLCHAR) {\r
6162       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6163       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6164     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6165       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6166       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6167     }\r
6168 \r
6169     if (appData.icsActive) {\r
6170       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6171     }\r
6172     else if (appData.noChessProgram) {\r
6173       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6174     }\r
6175     else {\r
6176       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6177     }\r
6178 \r
6179     SetStartupDialogEnables(hDlg);\r
6180     return TRUE;\r
6181 \r
6182   case WM_COMMAND:\r
6183     switch (LOWORD(wParam)) {\r
6184     case IDOK:\r
6185       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6186         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6187         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6188         p = buf;\r
6189         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6190         ParseArgs(StringGet, &p);\r
6191         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6192         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6193         p = buf;\r
6194         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6195         ParseArgs(StringGet, &p);\r
6196         SwapEngines(singleList); // ... and then make it 'second'\r
6197         appData.noChessProgram = FALSE;\r
6198         appData.icsActive = FALSE;\r
6199       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6200         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6201         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6202         p = buf;\r
6203         ParseArgs(StringGet, &p);\r
6204         if (appData.zippyPlay) {\r
6205           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6206           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6207           p = buf;\r
6208           ParseArgs(StringGet, &p);\r
6209         }\r
6210       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6211         appData.noChessProgram = TRUE;\r
6212         appData.icsActive = FALSE;\r
6213       } else {\r
6214         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6215                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6216         return TRUE;\r
6217       }\r
6218       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6219         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6220         p = buf;\r
6221         ParseArgs(StringGet, &p);\r
6222       }\r
6223       EndDialog(hDlg, TRUE);\r
6224       return TRUE;\r
6225 \r
6226     case IDCANCEL:\r
6227       ExitEvent(0);\r
6228       return TRUE;\r
6229 \r
6230     case IDM_HELPCONTENTS:\r
6231       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6232         MessageBox (GetFocus(),\r
6233                     _("Unable to activate help"),\r
6234                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6235       }\r
6236       break;\r
6237 \r
6238     default:\r
6239       SetStartupDialogEnables(hDlg);\r
6240       break;\r
6241     }\r
6242     break;\r
6243   }\r
6244   return FALSE;\r
6245 }\r
6246 \r
6247 /*---------------------------------------------------------------------------*\\r
6248  *\r
6249  * About box dialog functions\r
6250  *\r
6251 \*---------------------------------------------------------------------------*/\r
6252 \r
6253 /* Process messages for "About" dialog box */\r
6254 LRESULT CALLBACK\r
6255 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6256 {\r
6257   switch (message) {\r
6258   case WM_INITDIALOG: /* message: initialize dialog box */\r
6259     /* Center the dialog over the application window */\r
6260     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6261     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6262     Translate(hDlg, ABOUTBOX);\r
6263     JAWS_COPYRIGHT\r
6264     return (TRUE);\r
6265 \r
6266   case WM_COMMAND: /* message: received a command */\r
6267     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6268         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6269       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6270       return (TRUE);\r
6271     }\r
6272     break;\r
6273   }\r
6274   return (FALSE);\r
6275 }\r
6276 \r
6277 /*---------------------------------------------------------------------------*\\r
6278  *\r
6279  * Comment Dialog functions\r
6280  *\r
6281 \*---------------------------------------------------------------------------*/\r
6282 \r
6283 LRESULT CALLBACK\r
6284 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6285 {\r
6286   static HANDLE hwndText = NULL;\r
6287   int len, newSizeX, newSizeY, flags;\r
6288   static int sizeX, sizeY;\r
6289   char *str;\r
6290   RECT rect;\r
6291   MINMAXINFO *mmi;\r
6292 \r
6293   switch (message) {\r
6294   case WM_INITDIALOG: /* message: initialize dialog box */\r
6295     /* Initialize the dialog items */\r
6296     Translate(hDlg, DLG_EditComment);\r
6297     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6298     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6299     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6300     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6301     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6302     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6303     SetWindowText(hDlg, commentTitle);\r
6304     if (editComment) {\r
6305       SetFocus(hwndText);\r
6306     } else {\r
6307       SetFocus(GetDlgItem(hDlg, IDOK));\r
6308     }\r
6309     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6310                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6311                 MAKELPARAM(FALSE, 0));\r
6312     /* Size and position the dialog */\r
6313     if (!commentDialog) {\r
6314       commentDialog = hDlg;\r
6315       flags = SWP_NOZORDER;\r
6316       GetClientRect(hDlg, &rect);\r
6317       sizeX = rect.right;\r
6318       sizeY = rect.bottom;\r
6319       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6320           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6321         WINDOWPLACEMENT wp;\r
6322         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6323         wp.length = sizeof(WINDOWPLACEMENT);\r
6324         wp.flags = 0;\r
6325         wp.showCmd = SW_SHOW;\r
6326         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6327         wp.rcNormalPosition.left = wpComment.x;\r
6328         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6329         wp.rcNormalPosition.top = wpComment.y;\r
6330         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6331         SetWindowPlacement(hDlg, &wp);\r
6332 \r
6333         GetClientRect(hDlg, &rect);\r
6334         newSizeX = rect.right;\r
6335         newSizeY = rect.bottom;\r
6336         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6337                               newSizeX, newSizeY);\r
6338         sizeX = newSizeX;\r
6339         sizeY = newSizeY;\r
6340       }\r
6341     }\r
6342     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6343     return FALSE;\r
6344 \r
6345   case WM_COMMAND: /* message: received a command */\r
6346     switch (LOWORD(wParam)) {\r
6347     case IDOK:\r
6348       if (editComment) {\r
6349         char *p, *q;\r
6350         /* Read changed options from the dialog box */\r
6351         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6352         len = GetWindowTextLength(hwndText);\r
6353         str = (char *) malloc(len + 1);\r
6354         GetWindowText(hwndText, str, len + 1);\r
6355         p = q = str;\r
6356         while (*q) {\r
6357           if (*q == '\r')\r
6358             q++;\r
6359           else\r
6360             *p++ = *q++;\r
6361         }\r
6362         *p = NULLCHAR;\r
6363         ReplaceComment(commentIndex, str);\r
6364         free(str);\r
6365       }\r
6366       CommentPopDown();\r
6367       return TRUE;\r
6368 \r
6369     case IDCANCEL:\r
6370     case OPT_CancelComment:\r
6371       CommentPopDown();\r
6372       return TRUE;\r
6373 \r
6374     case OPT_ClearComment:\r
6375       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6376       break;\r
6377 \r
6378     case OPT_EditComment:\r
6379       EditCommentEvent();\r
6380       return TRUE;\r
6381 \r
6382     default:\r
6383       break;\r
6384     }\r
6385     break;\r
6386 \r
6387   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6388         if( wParam == OPT_CommentText ) {\r
6389             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6390 \r
6391             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6392                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6393                 POINTL pt;\r
6394                 LRESULT index;\r
6395 \r
6396                 pt.x = LOWORD( lpMF->lParam );\r
6397                 pt.y = HIWORD( lpMF->lParam );\r
6398 \r
6399                 if(lpMF->msg == WM_CHAR) {\r
6400                         CHARRANGE sel;\r
6401                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6402                         index = sel.cpMin;\r
6403                 } else\r
6404                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6405 \r
6406                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6407                 len = GetWindowTextLength(hwndText);\r
6408                 str = (char *) malloc(len + 1);\r
6409                 GetWindowText(hwndText, str, len + 1);\r
6410                 ReplaceComment(commentIndex, str);\r
6411                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6412                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6413                 free(str);\r
6414 \r
6415                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6416                 lpMF->msg = WM_USER;\r
6417 \r
6418                 return TRUE;\r
6419             }\r
6420         }\r
6421         break;\r
6422 \r
6423   case WM_SIZE:\r
6424     newSizeX = LOWORD(lParam);\r
6425     newSizeY = HIWORD(lParam);\r
6426     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6427     sizeX = newSizeX;\r
6428     sizeY = newSizeY;\r
6429     break;\r
6430 \r
6431   case WM_GETMINMAXINFO:\r
6432     /* Prevent resizing window too small */\r
6433     mmi = (MINMAXINFO *) lParam;\r
6434     mmi->ptMinTrackSize.x = 100;\r
6435     mmi->ptMinTrackSize.y = 100;\r
6436     break;\r
6437   }\r
6438   return FALSE;\r
6439 }\r
6440 \r
6441 VOID\r
6442 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6443 {\r
6444   FARPROC lpProc;\r
6445   char *p, *q;\r
6446 \r
6447   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6448 \r
6449   if (str == NULL) str = "";\r
6450   p = (char *) malloc(2 * strlen(str) + 2);\r
6451   q = p;\r
6452   while (*str) {\r
6453     if (*str == '\n') *q++ = '\r';\r
6454     *q++ = *str++;\r
6455   }\r
6456   *q = NULLCHAR;\r
6457   if (commentText != NULL) free(commentText);\r
6458 \r
6459   commentIndex = index;\r
6460   commentTitle = title;\r
6461   commentText = p;\r
6462   editComment = edit;\r
6463 \r
6464   if (commentDialog) {\r
6465     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6466     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6467   } else {\r
6468     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6469     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6470                  hwndMain, (DLGPROC)lpProc);\r
6471     FreeProcInstance(lpProc);\r
6472   }\r
6473   commentUp = TRUE;\r
6474 }\r
6475 \r
6476 \r
6477 /*---------------------------------------------------------------------------*\\r
6478  *\r
6479  * Type-in move dialog functions\r
6480  * \r
6481 \*---------------------------------------------------------------------------*/\r
6482 \r
6483 LRESULT CALLBACK\r
6484 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6485 {\r
6486   char move[MSG_SIZ];\r
6487   HWND hInput;\r
6488 \r
6489   switch (message) {\r
6490   case WM_INITDIALOG:\r
6491     move[0] = (char) lParam;\r
6492     move[1] = NULLCHAR;\r
6493     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6494     Translate(hDlg, DLG_TypeInMove);\r
6495     hInput = GetDlgItem(hDlg, OPT_Move);\r
6496     SetWindowText(hInput, move);\r
6497     SetFocus(hInput);\r
6498     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6499     return FALSE;\r
6500 \r
6501   case WM_COMMAND:\r
6502     switch (LOWORD(wParam)) {\r
6503     case IDOK:\r
6504 \r
6505       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6506       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6507       TypeInDoneEvent(move);\r
6508       EndDialog(hDlg, TRUE);\r
6509       return TRUE;\r
6510     case IDCANCEL:\r
6511       EndDialog(hDlg, FALSE);\r
6512       return TRUE;\r
6513     default:\r
6514       break;\r
6515     }\r
6516     break;\r
6517   }\r
6518   return FALSE;\r
6519 }\r
6520 \r
6521 VOID\r
6522 PopUpMoveDialog(char firstchar)\r
6523 {\r
6524     FARPROC lpProc;\r
6525 \r
6526       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6527       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6528         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6529       FreeProcInstance(lpProc);\r
6530 }\r
6531 \r
6532 /*---------------------------------------------------------------------------*\\r
6533  *\r
6534  * Type-in name dialog functions\r
6535  * \r
6536 \*---------------------------------------------------------------------------*/\r
6537 \r
6538 LRESULT CALLBACK\r
6539 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6540 {\r
6541   char move[MSG_SIZ];\r
6542   HWND hInput;\r
6543 \r
6544   switch (message) {\r
6545   case WM_INITDIALOG:\r
6546     move[0] = (char) lParam;\r
6547     move[1] = NULLCHAR;\r
6548     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6549     Translate(hDlg, DLG_TypeInName);\r
6550     hInput = GetDlgItem(hDlg, OPT_Name);\r
6551     SetWindowText(hInput, move);\r
6552     SetFocus(hInput);\r
6553     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6554     return FALSE;\r
6555 \r
6556   case WM_COMMAND:\r
6557     switch (LOWORD(wParam)) {\r
6558     case IDOK:\r
6559       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6560       appData.userName = strdup(move);\r
6561       SetUserLogo();\r
6562       SetGameInfo();\r
6563       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6564         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6565         DisplayTitle(move);\r
6566       }\r
6567 \r
6568 \r
6569       EndDialog(hDlg, TRUE);\r
6570       return TRUE;\r
6571     case IDCANCEL:\r
6572       EndDialog(hDlg, FALSE);\r
6573       return TRUE;\r
6574     default:\r
6575       break;\r
6576     }\r
6577     break;\r
6578   }\r
6579   return FALSE;\r
6580 }\r
6581 \r
6582 VOID\r
6583 PopUpNameDialog(char firstchar)\r
6584 {\r
6585     FARPROC lpProc;\r
6586     \r
6587       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6588       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6589         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6590       FreeProcInstance(lpProc);\r
6591 }\r
6592 \r
6593 /*---------------------------------------------------------------------------*\\r
6594  *\r
6595  *  Error dialogs\r
6596  * \r
6597 \*---------------------------------------------------------------------------*/\r
6598 \r
6599 /* Nonmodal error box */\r
6600 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6601                              WPARAM wParam, LPARAM lParam);\r
6602 \r
6603 VOID\r
6604 ErrorPopUp(char *title, char *content)\r
6605 {\r
6606   FARPROC lpProc;\r
6607   char *p, *q;\r
6608   BOOLEAN modal = hwndMain == NULL;\r
6609 \r
6610   p = content;\r
6611   q = errorMessage;\r
6612   while (*p) {\r
6613     if (*p == '\n') {\r
6614       if (modal) {\r
6615         *q++ = ' ';\r
6616         p++;\r
6617       } else {\r
6618         *q++ = '\r';\r
6619         *q++ = *p++;\r
6620       }\r
6621     } else {\r
6622       *q++ = *p++;\r
6623     }\r
6624   }\r
6625   *q = NULLCHAR;\r
6626   strncpy(errorTitle, title, sizeof(errorTitle));\r
6627   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6628   \r
6629   if (modal) {\r
6630     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6631   } else {\r
6632     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6633     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6634                  hwndMain, (DLGPROC)lpProc);\r
6635     FreeProcInstance(lpProc);\r
6636   }\r
6637 }\r
6638 \r
6639 VOID\r
6640 ErrorPopDown()\r
6641 {\r
6642   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6643   if (errorDialog == NULL) return;\r
6644   DestroyWindow(errorDialog);\r
6645   errorDialog = NULL;\r
6646   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6647 }\r
6648 \r
6649 LRESULT CALLBACK\r
6650 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6651 {\r
6652   HANDLE hwndText;\r
6653   RECT rChild;\r
6654 \r
6655   switch (message) {\r
6656   case WM_INITDIALOG:\r
6657     GetWindowRect(hDlg, &rChild);\r
6658 \r
6659     /*\r
6660     SetWindowPos(hDlg, NULL, rChild.left,\r
6661       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6662       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6663     */\r
6664 \r
6665     /* \r
6666         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6667         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6668         and it doesn't work when you resize the dialog.\r
6669         For now, just give it a default position.\r
6670     */\r
6671     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6672     Translate(hDlg, DLG_Error);\r
6673 \r
6674     errorDialog = hDlg;\r
6675     SetWindowText(hDlg, errorTitle);\r
6676     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6677     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6678     return FALSE;\r
6679 \r
6680   case WM_COMMAND:\r
6681     switch (LOWORD(wParam)) {\r
6682     case IDOK:\r
6683     case IDCANCEL:\r
6684       if (errorDialog == hDlg) errorDialog = NULL;\r
6685       DestroyWindow(hDlg);\r
6686       return TRUE;\r
6687 \r
6688     default:\r
6689       break;\r
6690     }\r
6691     break;\r
6692   }\r
6693   return FALSE;\r
6694 }\r
6695 \r
6696 #ifdef GOTHIC\r
6697 HWND gothicDialog = NULL;\r
6698 \r
6699 LRESULT CALLBACK\r
6700 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6701 {\r
6702   HANDLE hwndText;\r
6703   RECT rChild;\r
6704   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6705 \r
6706   switch (message) {\r
6707   case WM_INITDIALOG:\r
6708     GetWindowRect(hDlg, &rChild);\r
6709 \r
6710     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6711                                                              SWP_NOZORDER);\r
6712 \r
6713     /* \r
6714         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6715         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6716         and it doesn't work when you resize the dialog.\r
6717         For now, just give it a default position.\r
6718     */\r
6719     gothicDialog = hDlg;\r
6720     SetWindowText(hDlg, errorTitle);\r
6721     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6722     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6723     return FALSE;\r
6724 \r
6725   case WM_COMMAND:\r
6726     switch (LOWORD(wParam)) {\r
6727     case IDOK:\r
6728     case IDCANCEL:\r
6729       if (errorDialog == hDlg) errorDialog = NULL;\r
6730       DestroyWindow(hDlg);\r
6731       return TRUE;\r
6732 \r
6733     default:\r
6734       break;\r
6735     }\r
6736     break;\r
6737   }\r
6738   return FALSE;\r
6739 }\r
6740 \r
6741 VOID\r
6742 GothicPopUp(char *title, VariantClass variant)\r
6743 {\r
6744   FARPROC lpProc;\r
6745   static char *lastTitle;\r
6746 \r
6747   strncpy(errorTitle, title, sizeof(errorTitle));\r
6748   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6749 \r
6750   if(lastTitle != title && gothicDialog != NULL) {\r
6751     DestroyWindow(gothicDialog);\r
6752     gothicDialog = NULL;\r
6753   }\r
6754   if(variant != VariantNormal && gothicDialog == NULL) {\r
6755     title = lastTitle;\r
6756     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6757     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6758                  hwndMain, (DLGPROC)lpProc);\r
6759     FreeProcInstance(lpProc);\r
6760   }\r
6761 }\r
6762 #endif\r
6763 \r
6764 /*---------------------------------------------------------------------------*\\r
6765  *\r
6766  *  Ics Interaction console functions\r
6767  *\r
6768 \*---------------------------------------------------------------------------*/\r
6769 \r
6770 #define HISTORY_SIZE 64\r
6771 static char *history[HISTORY_SIZE];\r
6772 int histIn = 0, histP = 0;\r
6773 \r
6774 VOID\r
6775 SaveInHistory(char *cmd)\r
6776 {\r
6777   if (history[histIn] != NULL) {\r
6778     free(history[histIn]);\r
6779     history[histIn] = NULL;\r
6780   }\r
6781   if (*cmd == NULLCHAR) return;\r
6782   history[histIn] = StrSave(cmd);\r
6783   histIn = (histIn + 1) % HISTORY_SIZE;\r
6784   if (history[histIn] != NULL) {\r
6785     free(history[histIn]);\r
6786     history[histIn] = NULL;\r
6787   }\r
6788   histP = histIn;\r
6789 }\r
6790 \r
6791 char *\r
6792 PrevInHistory(char *cmd)\r
6793 {\r
6794   int newhp;\r
6795   if (histP == histIn) {\r
6796     if (history[histIn] != NULL) free(history[histIn]);\r
6797     history[histIn] = StrSave(cmd);\r
6798   }\r
6799   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6800   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6801   histP = newhp;\r
6802   return history[histP];\r
6803 }\r
6804 \r
6805 char *\r
6806 NextInHistory()\r
6807 {\r
6808   if (histP == histIn) return NULL;\r
6809   histP = (histP + 1) % HISTORY_SIZE;\r
6810   return history[histP];   \r
6811 }\r
6812 \r
6813 HMENU\r
6814 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6815 {\r
6816   HMENU hmenu, h;\r
6817   int i = 0;\r
6818   hmenu = LoadMenu(hInst, "TextMenu");\r
6819   h = GetSubMenu(hmenu, 0);\r
6820   while (e->item) {\r
6821     if (strcmp(e->item, "-") == 0) {\r
6822       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6823     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6824       int flags = MF_STRING, j = 0;\r
6825       if (e->item[0] == '|') {\r
6826         flags |= MF_MENUBARBREAK;\r
6827         j++;\r
6828       }\r
6829       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6830       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6831     }\r
6832     e++;\r
6833     i++;\r
6834   } \r
6835   return hmenu;\r
6836 }\r
6837 \r
6838 WNDPROC consoleTextWindowProc;\r
6839 \r
6840 void\r
6841 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6842 {\r
6843   char buf[MSG_SIZ], name[MSG_SIZ];\r
6844   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6845   CHARRANGE sel;\r
6846 \r
6847   if (!getname) {\r
6848     SetWindowText(hInput, command);\r
6849     if (immediate) {\r
6850       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6851     } else {\r
6852       sel.cpMin = 999999;\r
6853       sel.cpMax = 999999;\r
6854       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6855       SetFocus(hInput);\r
6856     }\r
6857     return;\r
6858   }    \r
6859   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6860   if (sel.cpMin == sel.cpMax) {\r
6861     /* Expand to surrounding word */\r
6862     TEXTRANGE tr;\r
6863     do {\r
6864       tr.chrg.cpMax = sel.cpMin;\r
6865       tr.chrg.cpMin = --sel.cpMin;\r
6866       if (sel.cpMin < 0) break;\r
6867       tr.lpstrText = name;\r
6868       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6869     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6870     sel.cpMin++;\r
6871 \r
6872     do {\r
6873       tr.chrg.cpMin = sel.cpMax;\r
6874       tr.chrg.cpMax = ++sel.cpMax;\r
6875       tr.lpstrText = name;\r
6876       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6877     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6878     sel.cpMax--;\r
6879 \r
6880     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6881       MessageBeep(MB_ICONEXCLAMATION);\r
6882       return;\r
6883     }\r
6884     tr.chrg = sel;\r
6885     tr.lpstrText = name;\r
6886     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6887   } else {\r
6888     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6889       MessageBeep(MB_ICONEXCLAMATION);\r
6890       return;\r
6891     }\r
6892     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6893   }\r
6894   if (immediate) {\r
6895     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6896     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6897     SetWindowText(hInput, buf);\r
6898     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6899   } else {\r
6900     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6901       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6902     SetWindowText(hInput, buf);\r
6903     sel.cpMin = 999999;\r
6904     sel.cpMax = 999999;\r
6905     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6906     SetFocus(hInput);\r
6907   }\r
6908 }\r
6909 \r
6910 LRESULT CALLBACK \r
6911 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6912 {\r
6913   HWND hInput;\r
6914   CHARRANGE sel;\r
6915 \r
6916   switch (message) {\r
6917   case WM_KEYDOWN:\r
6918     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6919     if(wParam=='R') return 0;\r
6920     switch (wParam) {\r
6921     case VK_PRIOR:\r
6922       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6923       return 0;\r
6924     case VK_NEXT:\r
6925       sel.cpMin = 999999;\r
6926       sel.cpMax = 999999;\r
6927       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6928       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6929       return 0;\r
6930     }\r
6931     break;\r
6932   case WM_CHAR:\r
6933    if(wParam != '\022') {\r
6934     if (wParam == '\t') {\r
6935       if (GetKeyState(VK_SHIFT) < 0) {\r
6936         /* shifted */\r
6937         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6938         if (buttonDesc[0].hwnd) {\r
6939           SetFocus(buttonDesc[0].hwnd);\r
6940         } else {\r
6941           SetFocus(hwndMain);\r
6942         }\r
6943       } else {\r
6944         /* unshifted */\r
6945         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6946       }\r
6947     } else {\r
6948       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6949       JAWS_DELETE( SetFocus(hInput); )\r
6950       SendMessage(hInput, message, wParam, lParam);\r
6951     }\r
6952     return 0;\r
6953    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6954    lParam = -1;\r
6955   case WM_RBUTTONDOWN:\r
6956     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6957       /* Move selection here if it was empty */\r
6958       POINT pt;\r
6959       pt.x = LOWORD(lParam);\r
6960       pt.y = HIWORD(lParam);\r
6961       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6962       if (sel.cpMin == sel.cpMax) {\r
6963         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6964         sel.cpMax = sel.cpMin;\r
6965         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6966       }\r
6967       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6968 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6969       POINT pt;\r
6970       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6971       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6972       if (sel.cpMin == sel.cpMax) {\r
6973         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6974         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6975       }\r
6976       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6977         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6978       }\r
6979       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6980       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6981       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6982       MenuPopup(hwnd, pt, hmenu, -1);\r
6983 }\r
6984     }\r
6985     return 0;\r
6986   case WM_RBUTTONUP:\r
6987     if (GetKeyState(VK_SHIFT) & ~1) {\r
6988       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6989         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6990     }\r
6991     return 0;\r
6992   case WM_PASTE:\r
6993     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6994     SetFocus(hInput);\r
6995     return SendMessage(hInput, message, wParam, lParam);\r
6996   case WM_MBUTTONDOWN:\r
6997     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6998   case WM_COMMAND:\r
6999     switch (LOWORD(wParam)) {\r
7000     case IDM_QuickPaste:\r
7001       {\r
7002         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7003         if (sel.cpMin == sel.cpMax) {\r
7004           MessageBeep(MB_ICONEXCLAMATION);\r
7005           return 0;\r
7006         }\r
7007         SendMessage(hwnd, WM_COPY, 0, 0);\r
7008         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7009         SendMessage(hInput, WM_PASTE, 0, 0);\r
7010         SetFocus(hInput);\r
7011         return 0;\r
7012       }\r
7013     case IDM_Cut:\r
7014       SendMessage(hwnd, WM_CUT, 0, 0);\r
7015       return 0;\r
7016     case IDM_Paste:\r
7017       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7018       return 0;\r
7019     case IDM_Copy:\r
7020       SendMessage(hwnd, WM_COPY, 0, 0);\r
7021       return 0;\r
7022     default:\r
7023       {\r
7024         int i = LOWORD(wParam) - IDM_CommandX;\r
7025         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7026             icsTextMenuEntry[i].command != NULL) {\r
7027           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7028                    icsTextMenuEntry[i].getname,\r
7029                    icsTextMenuEntry[i].immediate);\r
7030           return 0;\r
7031         }\r
7032       }\r
7033       break;\r
7034     }\r
7035     break;\r
7036   }\r
7037   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7038 }\r
7039 \r
7040 WNDPROC consoleInputWindowProc;\r
7041 \r
7042 LRESULT CALLBACK\r
7043 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7044 {\r
7045   char buf[MSG_SIZ];\r
7046   char *p;\r
7047   static BOOL sendNextChar = FALSE;\r
7048   static BOOL quoteNextChar = FALSE;\r
7049   InputSource *is = consoleInputSource;\r
7050   CHARFORMAT cf;\r
7051   CHARRANGE sel;\r
7052 \r
7053   switch (message) {\r
7054   case WM_CHAR:\r
7055     if (!appData.localLineEditing || sendNextChar) {\r
7056       is->buf[0] = (CHAR) wParam;\r
7057       is->count = 1;\r
7058       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7059       sendNextChar = FALSE;\r
7060       return 0;\r
7061     }\r
7062     if (quoteNextChar) {\r
7063       buf[0] = (char) wParam;\r
7064       buf[1] = NULLCHAR;\r
7065       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7066       quoteNextChar = FALSE;\r
7067       return 0;\r
7068     }\r
7069     switch (wParam) {\r
7070     case '\r':   /* Enter key */\r
7071       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7072       if (consoleEcho) SaveInHistory(is->buf);\r
7073       is->buf[is->count++] = '\n';\r
7074       is->buf[is->count] = NULLCHAR;\r
7075       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7076       if (consoleEcho) {\r
7077         ConsoleOutput(is->buf, is->count, TRUE);\r
7078       } else if (appData.localLineEditing) {\r
7079         ConsoleOutput("\n", 1, TRUE);\r
7080       }\r
7081       /* fall thru */\r
7082     case '\033': /* Escape key */\r
7083       SetWindowText(hwnd, "");\r
7084       cf.cbSize = sizeof(CHARFORMAT);\r
7085       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7086       if (consoleEcho) {\r
7087         cf.crTextColor = textAttribs[ColorNormal].color;\r
7088       } else {\r
7089         cf.crTextColor = COLOR_ECHOOFF;\r
7090       }\r
7091       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7092       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7093       return 0;\r
7094     case '\t':   /* Tab key */\r
7095       if (GetKeyState(VK_SHIFT) < 0) {\r
7096         /* shifted */\r
7097         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7098       } else {\r
7099         /* unshifted */\r
7100         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7101         if (buttonDesc[0].hwnd) {\r
7102           SetFocus(buttonDesc[0].hwnd);\r
7103         } else {\r
7104           SetFocus(hwndMain);\r
7105         }\r
7106       }\r
7107       return 0;\r
7108     case '\023': /* Ctrl+S */\r
7109       sendNextChar = TRUE;\r
7110       return 0;\r
7111     case '\021': /* Ctrl+Q */\r
7112       quoteNextChar = TRUE;\r
7113       return 0;\r
7114     JAWS_REPLAY\r
7115     default:\r
7116       break;\r
7117     }\r
7118     break;\r
7119   case WM_KEYDOWN:\r
7120     switch (wParam) {\r
7121     case VK_UP:\r
7122       GetWindowText(hwnd, buf, MSG_SIZ);\r
7123       p = PrevInHistory(buf);\r
7124       if (p != NULL) {\r
7125         SetWindowText(hwnd, p);\r
7126         sel.cpMin = 999999;\r
7127         sel.cpMax = 999999;\r
7128         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7129         return 0;\r
7130       }\r
7131       break;\r
7132     case VK_DOWN:\r
7133       p = NextInHistory();\r
7134       if (p != NULL) {\r
7135         SetWindowText(hwnd, p);\r
7136         sel.cpMin = 999999;\r
7137         sel.cpMax = 999999;\r
7138         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7139         return 0;\r
7140       }\r
7141       break;\r
7142     case VK_HOME:\r
7143     case VK_END:\r
7144       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7145       /* fall thru */\r
7146     case VK_PRIOR:\r
7147     case VK_NEXT:\r
7148       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7149       return 0;\r
7150     }\r
7151     break;\r
7152   case WM_MBUTTONDOWN:\r
7153     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7154       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7155     break;\r
7156   case WM_RBUTTONUP:\r
7157     if (GetKeyState(VK_SHIFT) & ~1) {\r
7158       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7159         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7160     } else {\r
7161       POINT pt;\r
7162       HMENU hmenu;\r
7163       hmenu = LoadMenu(hInst, "InputMenu");\r
7164       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7165       if (sel.cpMin == sel.cpMax) {\r
7166         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7167         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7168       }\r
7169       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7170         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7171       }\r
7172       pt.x = LOWORD(lParam);\r
7173       pt.y = HIWORD(lParam);\r
7174       MenuPopup(hwnd, pt, hmenu, -1);\r
7175     }\r
7176     return 0;\r
7177   case WM_COMMAND:\r
7178     switch (LOWORD(wParam)) { \r
7179     case IDM_Undo:\r
7180       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7181       return 0;\r
7182     case IDM_SelectAll:\r
7183       sel.cpMin = 0;\r
7184       sel.cpMax = -1; /*999999?*/\r
7185       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7186       return 0;\r
7187     case IDM_Cut:\r
7188       SendMessage(hwnd, WM_CUT, 0, 0);\r
7189       return 0;\r
7190     case IDM_Paste:\r
7191       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7192       return 0;\r
7193     case IDM_Copy:\r
7194       SendMessage(hwnd, WM_COPY, 0, 0);\r
7195       return 0;\r
7196     }\r
7197     break;\r
7198   }\r
7199   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7200 }\r
7201 \r
7202 #define CO_MAX  100000\r
7203 #define CO_TRIM   1000\r
7204 \r
7205 LRESULT CALLBACK\r
7206 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7207 {\r
7208   static SnapData sd;\r
7209   HWND hText, hInput;\r
7210   RECT rect;\r
7211   static int sizeX, sizeY;\r
7212   int newSizeX, newSizeY;\r
7213   MINMAXINFO *mmi;\r
7214   WORD wMask;\r
7215 \r
7216   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7217   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7218 \r
7219   switch (message) {\r
7220   case WM_NOTIFY:\r
7221     if (((NMHDR*)lParam)->code == EN_LINK)\r
7222     {\r
7223       ENLINK *pLink = (ENLINK*)lParam;\r
7224       if (pLink->msg == WM_LBUTTONUP)\r
7225       {\r
7226         TEXTRANGE tr;\r
7227 \r
7228         tr.chrg = pLink->chrg;\r
7229         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7230         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7231         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7232         free(tr.lpstrText);\r
7233       }\r
7234     }\r
7235     break;\r
7236   case WM_INITDIALOG: /* message: initialize dialog box */\r
7237     hwndConsole = hDlg;\r
7238     SetFocus(hInput);\r
7239     consoleTextWindowProc = (WNDPROC)\r
7240       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7241     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7242     consoleInputWindowProc = (WNDPROC)\r
7243       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7244     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7245     Colorize(ColorNormal, TRUE);\r
7246     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7247     ChangedConsoleFont();\r
7248     GetClientRect(hDlg, &rect);\r
7249     sizeX = rect.right;\r
7250     sizeY = rect.bottom;\r
7251     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7252         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7253       WINDOWPLACEMENT wp;\r
7254       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7255       wp.length = sizeof(WINDOWPLACEMENT);\r
7256       wp.flags = 0;\r
7257       wp.showCmd = SW_SHOW;\r
7258       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7259       wp.rcNormalPosition.left = wpConsole.x;\r
7260       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7261       wp.rcNormalPosition.top = wpConsole.y;\r
7262       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7263       SetWindowPlacement(hDlg, &wp);\r
7264     }\r
7265 \r
7266    // [HGM] Chessknight's change 2004-07-13\r
7267    else { /* Determine Defaults */\r
7268        WINDOWPLACEMENT wp;\r
7269        wpConsole.x = wpMain.width + 1;\r
7270        wpConsole.y = wpMain.y;\r
7271        wpConsole.width = screenWidth -  wpMain.width;\r
7272        wpConsole.height = wpMain.height;\r
7273        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7274        wp.length = sizeof(WINDOWPLACEMENT);\r
7275        wp.flags = 0;\r
7276        wp.showCmd = SW_SHOW;\r
7277        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7278        wp.rcNormalPosition.left = wpConsole.x;\r
7279        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7280        wp.rcNormalPosition.top = wpConsole.y;\r
7281        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7282        SetWindowPlacement(hDlg, &wp);\r
7283     }\r
7284 \r
7285    // Allow hText to highlight URLs and send notifications on them\r
7286    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7287    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7288    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7289    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7290 \r
7291     return FALSE;\r
7292 \r
7293   case WM_SETFOCUS:\r
7294     SetFocus(hInput);\r
7295     return 0;\r
7296 \r
7297   case WM_CLOSE:\r
7298     ExitEvent(0);\r
7299     /* not reached */\r
7300     break;\r
7301 \r
7302   case WM_SIZE:\r
7303     if (IsIconic(hDlg)) break;\r
7304     newSizeX = LOWORD(lParam);\r
7305     newSizeY = HIWORD(lParam);\r
7306     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7307       RECT rectText, rectInput;\r
7308       POINT pt;\r
7309       int newTextHeight, newTextWidth;\r
7310       GetWindowRect(hText, &rectText);\r
7311       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7312       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7313       if (newTextHeight < 0) {\r
7314         newSizeY += -newTextHeight;\r
7315         newTextHeight = 0;\r
7316       }\r
7317       SetWindowPos(hText, NULL, 0, 0,\r
7318         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7319       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7320       pt.x = rectInput.left;\r
7321       pt.y = rectInput.top + newSizeY - sizeY;\r
7322       ScreenToClient(hDlg, &pt);\r
7323       SetWindowPos(hInput, NULL, \r
7324         pt.x, pt.y, /* needs client coords */   \r
7325         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7326         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7327     }\r
7328     sizeX = newSizeX;\r
7329     sizeY = newSizeY;\r
7330     break;\r
7331 \r
7332   case WM_GETMINMAXINFO:\r
7333     /* Prevent resizing window too small */\r
7334     mmi = (MINMAXINFO *) lParam;\r
7335     mmi->ptMinTrackSize.x = 100;\r
7336     mmi->ptMinTrackSize.y = 100;\r
7337     break;\r
7338 \r
7339   /* [AS] Snapping */\r
7340   case WM_ENTERSIZEMOVE:\r
7341     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7342 \r
7343   case WM_SIZING:\r
7344     return OnSizing( &sd, hDlg, wParam, lParam );\r
7345 \r
7346   case WM_MOVING:\r
7347     return OnMoving( &sd, hDlg, wParam, lParam );\r
7348 \r
7349   case WM_EXITSIZEMOVE:\r
7350         UpdateICSWidth(hText);\r
7351     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7352   }\r
7353 \r
7354   return DefWindowProc(hDlg, message, wParam, lParam);\r
7355 }\r
7356 \r
7357 \r
7358 VOID\r
7359 ConsoleCreate()\r
7360 {\r
7361   HWND hCons;\r
7362   if (hwndConsole) return;\r
7363   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7364   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7365 }\r
7366 \r
7367 \r
7368 VOID\r
7369 ConsoleOutput(char* data, int length, int forceVisible)\r
7370 {\r
7371   HWND hText;\r
7372   int trim, exlen;\r
7373   char *p, *q;\r
7374   char buf[CO_MAX+1];\r
7375   POINT pEnd;\r
7376   RECT rect;\r
7377   static int delayLF = 0;\r
7378   CHARRANGE savesel, sel;\r
7379 \r
7380   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7381   p = data;\r
7382   q = buf;\r
7383   if (delayLF) {\r
7384     *q++ = '\r';\r
7385     *q++ = '\n';\r
7386     delayLF = 0;\r
7387   }\r
7388   while (length--) {\r
7389     if (*p == '\n') {\r
7390       if (*++p) {\r
7391         *q++ = '\r';\r
7392         *q++ = '\n';\r
7393       } else {\r
7394         delayLF = 1;\r
7395       }\r
7396     } else if (*p == '\007') {\r
7397        MyPlaySound(&sounds[(int)SoundBell]);\r
7398        p++;\r
7399     } else {\r
7400       *q++ = *p++;\r
7401     }\r
7402   }\r
7403   *q = NULLCHAR;\r
7404   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7405   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7406   /* Save current selection */\r
7407   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7408   exlen = GetWindowTextLength(hText);\r
7409   /* Find out whether current end of text is visible */\r
7410   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7411   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7412   /* Trim existing text if it's too long */\r
7413   if (exlen + (q - buf) > CO_MAX) {\r
7414     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7415     sel.cpMin = 0;\r
7416     sel.cpMax = trim;\r
7417     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7418     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7419     exlen -= trim;\r
7420     savesel.cpMin -= trim;\r
7421     savesel.cpMax -= trim;\r
7422     if (exlen < 0) exlen = 0;\r
7423     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7424     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7425   }\r
7426   /* Append the new text */\r
7427   sel.cpMin = exlen;\r
7428   sel.cpMax = exlen;\r
7429   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7430   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7431   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7432   if (forceVisible || exlen == 0 ||\r
7433       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7434        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7435     /* Scroll to make new end of text visible if old end of text\r
7436        was visible or new text is an echo of user typein */\r
7437     sel.cpMin = 9999999;\r
7438     sel.cpMax = 9999999;\r
7439     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7440     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7441     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7442     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7443   }\r
7444   if (savesel.cpMax == exlen || forceVisible) {\r
7445     /* Move insert point to new end of text if it was at the old\r
7446        end of text or if the new text is an echo of user typein */\r
7447     sel.cpMin = 9999999;\r
7448     sel.cpMax = 9999999;\r
7449     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7450   } else {\r
7451     /* Restore previous selection */\r
7452     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7453   }\r
7454   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7455 }\r
7456 \r
7457 /*---------*/\r
7458 \r
7459 \r
7460 void\r
7461 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7462 {\r
7463   char buf[100];\r
7464   char *str;\r
7465   COLORREF oldFg, oldBg;\r
7466   HFONT oldFont;\r
7467   RECT rect;\r
7468 \r
7469   if(copyNumber > 1)\r
7470     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7471 \r
7472   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7473   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7474   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7475 \r
7476   rect.left = x;\r
7477   rect.right = x + squareSize;\r
7478   rect.top  = y;\r
7479   rect.bottom = y + squareSize;\r
7480   str = buf;\r
7481 \r
7482   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7483                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7484              y, ETO_CLIPPED|ETO_OPAQUE,\r
7485              &rect, str, strlen(str), NULL);\r
7486 \r
7487   (void) SetTextColor(hdc, oldFg);\r
7488   (void) SetBkColor(hdc, oldBg);\r
7489   (void) SelectObject(hdc, oldFont);\r
7490 }\r
7491 \r
7492 void\r
7493 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7494               RECT *rect, char *color, char *flagFell)\r
7495 {\r
7496   char buf[100];\r
7497   char *str;\r
7498   COLORREF oldFg, oldBg;\r
7499   HFONT oldFont;\r
7500 \r
7501   if (twoBoards && partnerUp) return;\r
7502   if (appData.clockMode) {\r
7503     if (tinyLayout)\r
7504       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7505     else\r
7506       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7507     str = buf;\r
7508   } else {\r
7509     str = color;\r
7510   }\r
7511 \r
7512   if (highlight) {\r
7513     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7514     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7515   } else {\r
7516     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7517     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7518   }\r
7519   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7520 \r
7521   JAWS_SILENCE\r
7522 \r
7523   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7524              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7525              rect, str, strlen(str), NULL);\r
7526   if(logoHeight > 0 && appData.clockMode) {\r
7527       RECT r;\r
7528       str += strlen(color)+2;\r
7529       r.top = rect->top + logoHeight/2;\r
7530       r.left = rect->left;\r
7531       r.right = rect->right;\r
7532       r.bottom = rect->bottom;\r
7533       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7534                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7535                  &r, str, strlen(str), NULL);\r
7536   }\r
7537   (void) SetTextColor(hdc, oldFg);\r
7538   (void) SetBkColor(hdc, oldBg);\r
7539   (void) SelectObject(hdc, oldFont);\r
7540 }\r
7541 \r
7542 \r
7543 int\r
7544 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7545            OVERLAPPED *ovl)\r
7546 {\r
7547   int ok, err;\r
7548 \r
7549   /* [AS]  */\r
7550   if( count <= 0 ) {\r
7551     if (appData.debugMode) {\r
7552       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7553     }\r
7554 \r
7555     return ERROR_INVALID_USER_BUFFER;\r
7556   }\r
7557 \r
7558   ResetEvent(ovl->hEvent);\r
7559   ovl->Offset = ovl->OffsetHigh = 0;\r
7560   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7561   if (ok) {\r
7562     err = NO_ERROR;\r
7563   } else {\r
7564     err = GetLastError();\r
7565     if (err == ERROR_IO_PENDING) {\r
7566       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7567       if (ok)\r
7568         err = NO_ERROR;\r
7569       else\r
7570         err = GetLastError();\r
7571     }\r
7572   }\r
7573   return err;\r
7574 }\r
7575 \r
7576 int\r
7577 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7578             OVERLAPPED *ovl)\r
7579 {\r
7580   int ok, err;\r
7581 \r
7582   ResetEvent(ovl->hEvent);\r
7583   ovl->Offset = ovl->OffsetHigh = 0;\r
7584   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7585   if (ok) {\r
7586     err = NO_ERROR;\r
7587   } else {\r
7588     err = GetLastError();\r
7589     if (err == ERROR_IO_PENDING) {\r
7590       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7591       if (ok)\r
7592         err = NO_ERROR;\r
7593       else\r
7594         err = GetLastError();\r
7595     }\r
7596   }\r
7597   return err;\r
7598 }\r
7599 \r
7600 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7601 void CheckForInputBufferFull( InputSource * is )\r
7602 {\r
7603     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7604         /* Look for end of line */\r
7605         char * p = is->buf;\r
7606         \r
7607         while( p < is->next && *p != '\n' ) {\r
7608             p++;\r
7609         }\r
7610 \r
7611         if( p >= is->next ) {\r
7612             if (appData.debugMode) {\r
7613                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7614             }\r
7615 \r
7616             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7617             is->count = (DWORD) -1;\r
7618             is->next = is->buf;\r
7619         }\r
7620     }\r
7621 }\r
7622 \r
7623 DWORD\r
7624 InputThread(LPVOID arg)\r
7625 {\r
7626   InputSource *is;\r
7627   OVERLAPPED ovl;\r
7628 \r
7629   is = (InputSource *) arg;\r
7630   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7631   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7632   while (is->hThread != NULL) {\r
7633     is->error = DoReadFile(is->hFile, is->next,\r
7634                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7635                            &is->count, &ovl);\r
7636     if (is->error == NO_ERROR) {\r
7637       is->next += is->count;\r
7638     } else {\r
7639       if (is->error == ERROR_BROKEN_PIPE) {\r
7640         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7641         is->count = 0;\r
7642       } else {\r
7643         is->count = (DWORD) -1;\r
7644         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7645         break; \r
7646       }\r
7647     }\r
7648 \r
7649     CheckForInputBufferFull( is );\r
7650 \r
7651     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7652 \r
7653     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7654 \r
7655     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7656   }\r
7657 \r
7658   CloseHandle(ovl.hEvent);\r
7659   CloseHandle(is->hFile);\r
7660 \r
7661   if (appData.debugMode) {\r
7662     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7663   }\r
7664 \r
7665   return 0;\r
7666 }\r
7667 \r
7668 \r
7669 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7670 DWORD\r
7671 NonOvlInputThread(LPVOID arg)\r
7672 {\r
7673   InputSource *is;\r
7674   char *p, *q;\r
7675   int i;\r
7676   char prev;\r
7677 \r
7678   is = (InputSource *) arg;\r
7679   while (is->hThread != NULL) {\r
7680     is->error = ReadFile(is->hFile, is->next,\r
7681                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7682                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7683     if (is->error == NO_ERROR) {\r
7684       /* Change CRLF to LF */\r
7685       if (is->next > is->buf) {\r
7686         p = is->next - 1;\r
7687         i = is->count + 1;\r
7688       } else {\r
7689         p = is->next;\r
7690         i = is->count;\r
7691       }\r
7692       q = p;\r
7693       prev = NULLCHAR;\r
7694       while (i > 0) {\r
7695         if (prev == '\r' && *p == '\n') {\r
7696           *(q-1) = '\n';\r
7697           is->count--;\r
7698         } else { \r
7699           *q++ = *p;\r
7700         }\r
7701         prev = *p++;\r
7702         i--;\r
7703       }\r
7704       *q = NULLCHAR;\r
7705       is->next = q;\r
7706     } else {\r
7707       if (is->error == ERROR_BROKEN_PIPE) {\r
7708         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7709         is->count = 0; \r
7710       } else {\r
7711         is->count = (DWORD) -1;\r
7712       }\r
7713     }\r
7714 \r
7715     CheckForInputBufferFull( is );\r
7716 \r
7717     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7718 \r
7719     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7720 \r
7721     if (is->count < 0) break;  /* Quit on error */\r
7722   }\r
7723   CloseHandle(is->hFile);\r
7724   return 0;\r
7725 }\r
7726 \r
7727 DWORD\r
7728 SocketInputThread(LPVOID arg)\r
7729 {\r
7730   InputSource *is;\r
7731 \r
7732   is = (InputSource *) arg;\r
7733   while (is->hThread != NULL) {\r
7734     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7735     if ((int)is->count == SOCKET_ERROR) {\r
7736       is->count = (DWORD) -1;\r
7737       is->error = WSAGetLastError();\r
7738     } else {\r
7739       is->error = NO_ERROR;\r
7740       is->next += is->count;\r
7741       if (is->count == 0 && is->second == is) {\r
7742         /* End of file on stderr; quit with no message */\r
7743         break;\r
7744       }\r
7745     }\r
7746     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7747 \r
7748     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7749 \r
7750     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7751   }\r
7752   return 0;\r
7753 }\r
7754 \r
7755 VOID\r
7756 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7757 {\r
7758   InputSource *is;\r
7759 \r
7760   is = (InputSource *) lParam;\r
7761   if (is->lineByLine) {\r
7762     /* Feed in lines one by one */\r
7763     char *p = is->buf;\r
7764     char *q = p;\r
7765     while (q < is->next) {\r
7766       if (*q++ == '\n') {\r
7767         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7768         p = q;\r
7769       }\r
7770     }\r
7771     \r
7772     /* Move any partial line to the start of the buffer */\r
7773     q = is->buf;\r
7774     while (p < is->next) {\r
7775       *q++ = *p++;\r
7776     }\r
7777     is->next = q;\r
7778 \r
7779     if (is->error != NO_ERROR || is->count == 0) {\r
7780       /* Notify backend of the error.  Note: If there was a partial\r
7781          line at the end, it is not flushed through. */\r
7782       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7783     }\r
7784   } else {\r
7785     /* Feed in the whole chunk of input at once */\r
7786     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7787     is->next = is->buf;\r
7788   }\r
7789 }\r
7790 \r
7791 /*---------------------------------------------------------------------------*\\r
7792  *\r
7793  *  Menu enables. Used when setting various modes.\r
7794  *\r
7795 \*---------------------------------------------------------------------------*/\r
7796 \r
7797 typedef struct {\r
7798   int item;\r
7799   int flags;\r
7800 } Enables;\r
7801 \r
7802 VOID\r
7803 GreyRevert(Boolean grey)\r
7804 { // [HGM] vari: for retracting variations in local mode\r
7805   HMENU hmenu = GetMenu(hwndMain);\r
7806   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7807   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7808 }\r
7809 \r
7810 VOID\r
7811 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7812 {\r
7813   while (enab->item > 0) {\r
7814     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7815     enab++;\r
7816   }\r
7817 }\r
7818 \r
7819 Enables gnuEnables[] = {\r
7820   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7833 \r
7834   // Needed to switch from ncp to GNU mode on Engine Load\r
7835   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7836   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7837   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7844   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7848   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7849   { -1, -1 }\r
7850 };\r
7851 \r
7852 Enables icsEnables[] = {\r
7853   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7873   { -1, -1 }\r
7874 };\r
7875 \r
7876 #if ZIPPY\r
7877 Enables zippyEnables[] = {\r
7878   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7882   { -1, -1 }\r
7883 };\r
7884 #endif\r
7885 \r
7886 Enables ncpEnables[] = {\r
7887   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7896   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7897   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7909   { -1, -1 }\r
7910 };\r
7911 \r
7912 Enables trainingOnEnables[] = {\r
7913   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7922   { -1, -1 }\r
7923 };\r
7924 \r
7925 Enables trainingOffEnables[] = {\r
7926   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7935   { -1, -1 }\r
7936 };\r
7937 \r
7938 /* These modify either ncpEnables or gnuEnables */\r
7939 Enables cmailEnables[] = {\r
7940   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7941   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7942   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7943   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7947   { -1, -1 }\r
7948 };\r
7949 \r
7950 Enables machineThinkingEnables[] = {\r
7951   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7964 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7967   { -1, -1 }\r
7968 };\r
7969 \r
7970 Enables userThinkingEnables[] = {\r
7971   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7984 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7987   { -1, -1 }\r
7988 };\r
7989 \r
7990 /*---------------------------------------------------------------------------*\\r
7991  *\r
7992  *  Front-end interface functions exported by XBoard.\r
7993  *  Functions appear in same order as prototypes in frontend.h.\r
7994  * \r
7995 \*---------------------------------------------------------------------------*/\r
7996 VOID\r
7997 CheckMark(UINT item, int state)\r
7998 {\r
7999     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8000 }\r
8001 \r
8002 VOID\r
8003 ModeHighlight()\r
8004 {\r
8005   static UINT prevChecked = 0;\r
8006   static int prevPausing = 0;\r
8007   UINT nowChecked;\r
8008 \r
8009   if (pausing != prevPausing) {\r
8010     prevPausing = pausing;\r
8011     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8012                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8013     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8014   }\r
8015 \r
8016   switch (gameMode) {\r
8017   case BeginningOfGame:\r
8018     if (appData.icsActive)\r
8019       nowChecked = IDM_IcsClient;\r
8020     else if (appData.noChessProgram)\r
8021       nowChecked = IDM_EditGame;\r
8022     else\r
8023       nowChecked = IDM_MachineBlack;\r
8024     break;\r
8025   case MachinePlaysBlack:\r
8026     nowChecked = IDM_MachineBlack;\r
8027     break;\r
8028   case MachinePlaysWhite:\r
8029     nowChecked = IDM_MachineWhite;\r
8030     break;\r
8031   case TwoMachinesPlay:\r
8032     nowChecked = IDM_TwoMachines;\r
8033     break;\r
8034   case AnalyzeMode:\r
8035     nowChecked = IDM_AnalysisMode;\r
8036     break;\r
8037   case AnalyzeFile:\r
8038     nowChecked = IDM_AnalyzeFile;\r
8039     break;\r
8040   case EditGame:\r
8041     nowChecked = IDM_EditGame;\r
8042     break;\r
8043   case PlayFromGameFile:\r
8044     nowChecked = IDM_LoadGame;\r
8045     break;\r
8046   case EditPosition:\r
8047     nowChecked = IDM_EditPosition;\r
8048     break;\r
8049   case Training:\r
8050     nowChecked = IDM_Training;\r
8051     break;\r
8052   case IcsPlayingWhite:\r
8053   case IcsPlayingBlack:\r
8054   case IcsObserving:\r
8055   case IcsIdle:\r
8056     nowChecked = IDM_IcsClient;\r
8057     break;\r
8058   default:\r
8059   case EndOfGame:\r
8060     nowChecked = 0;\r
8061     break;\r
8062   }\r
8063   CheckMark(prevChecked, MF_UNCHECKED);\r
8064   CheckMark(nowChecked, MF_CHECKED);\r
8065   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8066 \r
8067   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8068     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8069                           MF_BYCOMMAND|MF_ENABLED);\r
8070   } else {\r
8071     (void) EnableMenuItem(GetMenu(hwndMain), \r
8072                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8073   }\r
8074 \r
8075   prevChecked = nowChecked;\r
8076 \r
8077   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8078   if (appData.icsActive) {\r
8079        if (appData.icsEngineAnalyze) {\r
8080                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8081        } else {\r
8082                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8083        }\r
8084   }\r
8085   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8086 }\r
8087 \r
8088 VOID\r
8089 SetICSMode()\r
8090 {\r
8091   HMENU hmenu = GetMenu(hwndMain);\r
8092   SetMenuEnables(hmenu, icsEnables);\r
8093   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8094     MF_BYCOMMAND|MF_ENABLED);\r
8095 #if ZIPPY\r
8096   if (appData.zippyPlay) {\r
8097     SetMenuEnables(hmenu, zippyEnables);\r
8098     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8099          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8100           MF_BYCOMMAND|MF_ENABLED);\r
8101   }\r
8102 #endif\r
8103 }\r
8104 \r
8105 VOID\r
8106 SetGNUMode()\r
8107 {\r
8108   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8109 }\r
8110 \r
8111 VOID\r
8112 SetNCPMode()\r
8113 {\r
8114   HMENU hmenu = GetMenu(hwndMain);\r
8115   SetMenuEnables(hmenu, ncpEnables);\r
8116     DrawMenuBar(hwndMain);\r
8117 }\r
8118 \r
8119 VOID\r
8120 SetCmailMode()\r
8121 {\r
8122   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8123 }\r
8124 \r
8125 VOID \r
8126 SetTrainingModeOn()\r
8127 {\r
8128   int i;\r
8129   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8130   for (i = 0; i < N_BUTTONS; i++) {\r
8131     if (buttonDesc[i].hwnd != NULL)\r
8132       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8133   }\r
8134   CommentPopDown();\r
8135 }\r
8136 \r
8137 VOID SetTrainingModeOff()\r
8138 {\r
8139   int i;\r
8140   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8141   for (i = 0; i < N_BUTTONS; i++) {\r
8142     if (buttonDesc[i].hwnd != NULL)\r
8143       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8144   }\r
8145 }\r
8146 \r
8147 \r
8148 VOID\r
8149 SetUserThinkingEnables()\r
8150 {\r
8151   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8152 }\r
8153 \r
8154 VOID\r
8155 SetMachineThinkingEnables()\r
8156 {\r
8157   HMENU hMenu = GetMenu(hwndMain);\r
8158   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8159 \r
8160   SetMenuEnables(hMenu, machineThinkingEnables);\r
8161 \r
8162   if (gameMode == MachinePlaysBlack) {\r
8163     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8164   } else if (gameMode == MachinePlaysWhite) {\r
8165     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8166   } else if (gameMode == TwoMachinesPlay) {\r
8167     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8168   }\r
8169 }\r
8170 \r
8171 \r
8172 VOID\r
8173 DisplayTitle(char *str)\r
8174 {\r
8175   char title[MSG_SIZ], *host;\r
8176   if (str[0] != NULLCHAR) {\r
8177     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8178   } else if (appData.icsActive) {\r
8179     if (appData.icsCommPort[0] != NULLCHAR)\r
8180       host = "ICS";\r
8181     else \r
8182       host = appData.icsHost;\r
8183       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8184   } else if (appData.noChessProgram) {\r
8185     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8186   } else {\r
8187     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8188     strcat(title, ": ");\r
8189     strcat(title, first.tidy);\r
8190   }\r
8191   SetWindowText(hwndMain, title);\r
8192 }\r
8193 \r
8194 \r
8195 VOID\r
8196 DisplayMessage(char *str1, char *str2)\r
8197 {\r
8198   HDC hdc;\r
8199   HFONT oldFont;\r
8200   int remain = MESSAGE_TEXT_MAX - 1;\r
8201   int len;\r
8202 \r
8203   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8204   messageText[0] = NULLCHAR;\r
8205   if (*str1) {\r
8206     len = strlen(str1);\r
8207     if (len > remain) len = remain;\r
8208     strncpy(messageText, str1, len);\r
8209     messageText[len] = NULLCHAR;\r
8210     remain -= len;\r
8211   }\r
8212   if (*str2 && remain >= 2) {\r
8213     if (*str1) {\r
8214       strcat(messageText, "  ");\r
8215       remain -= 2;\r
8216     }\r
8217     len = strlen(str2);\r
8218     if (len > remain) len = remain;\r
8219     strncat(messageText, str2, len);\r
8220   }\r
8221   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8222   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8223 \r
8224   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8225 \r
8226   SAYMACHINEMOVE();\r
8227 \r
8228   hdc = GetDC(hwndMain);\r
8229   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8230   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8231              &messageRect, messageText, strlen(messageText), NULL);\r
8232   (void) SelectObject(hdc, oldFont);\r
8233   (void) ReleaseDC(hwndMain, hdc);\r
8234 }\r
8235 \r
8236 VOID\r
8237 DisplayError(char *str, int error)\r
8238 {\r
8239   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8240   int len;\r
8241 \r
8242   if (error == 0) {\r
8243     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8244   } else {\r
8245     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8246                         NULL, error, LANG_NEUTRAL,\r
8247                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8248     if (len > 0) {\r
8249       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8250     } else {\r
8251       ErrorMap *em = errmap;\r
8252       while (em->err != 0 && em->err != error) em++;\r
8253       if (em->err != 0) {\r
8254         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8255       } else {\r
8256         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8257       }\r
8258     }\r
8259   }\r
8260   \r
8261   ErrorPopUp(_("Error"), buf);\r
8262 }\r
8263 \r
8264 \r
8265 VOID\r
8266 DisplayMoveError(char *str)\r
8267 {\r
8268   fromX = fromY = -1;\r
8269   ClearHighlights();\r
8270   DrawPosition(FALSE, NULL);\r
8271   if (appData.popupMoveErrors) {\r
8272     ErrorPopUp(_("Error"), str);\r
8273   } else {\r
8274     DisplayMessage(str, "");\r
8275     moveErrorMessageUp = TRUE;\r
8276   }\r
8277 }\r
8278 \r
8279 VOID\r
8280 DisplayFatalError(char *str, int error, int exitStatus)\r
8281 {\r
8282   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8283   int len;\r
8284   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8285 \r
8286   if (error != 0) {\r
8287     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8288                         NULL, error, LANG_NEUTRAL,\r
8289                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8290     if (len > 0) {\r
8291       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8292     } else {\r
8293       ErrorMap *em = errmap;\r
8294       while (em->err != 0 && em->err != error) em++;\r
8295       if (em->err != 0) {\r
8296         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8297       } else {\r
8298         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8299       }\r
8300     }\r
8301     str = buf;\r
8302   }\r
8303   if (appData.debugMode) {\r
8304     fprintf(debugFP, "%s: %s\n", label, str);\r
8305   }\r
8306   if (appData.popupExitMessage) {\r
8307     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8308                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8309   }\r
8310   ExitEvent(exitStatus);\r
8311 }\r
8312 \r
8313 \r
8314 VOID\r
8315 DisplayInformation(char *str)\r
8316 {\r
8317   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8318 }\r
8319 \r
8320 \r
8321 VOID\r
8322 DisplayNote(char *str)\r
8323 {\r
8324   ErrorPopUp(_("Note"), str);\r
8325 }\r
8326 \r
8327 \r
8328 typedef struct {\r
8329   char *title, *question, *replyPrefix;\r
8330   ProcRef pr;\r
8331 } QuestionParams;\r
8332 \r
8333 LRESULT CALLBACK\r
8334 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8335 {\r
8336   static QuestionParams *qp;\r
8337   char reply[MSG_SIZ];\r
8338   int len, err;\r
8339 \r
8340   switch (message) {\r
8341   case WM_INITDIALOG:\r
8342     qp = (QuestionParams *) lParam;\r
8343     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8344     Translate(hDlg, DLG_Question);\r
8345     SetWindowText(hDlg, qp->title);\r
8346     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8347     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8348     return FALSE;\r
8349 \r
8350   case WM_COMMAND:\r
8351     switch (LOWORD(wParam)) {\r
8352     case IDOK:\r
8353       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8354       if (*reply) strcat(reply, " ");\r
8355       len = strlen(reply);\r
8356       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8357       strcat(reply, "\n");\r
8358       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8359       EndDialog(hDlg, TRUE);\r
8360       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8361       return TRUE;\r
8362     case IDCANCEL:\r
8363       EndDialog(hDlg, FALSE);\r
8364       return TRUE;\r
8365     default:\r
8366       break;\r
8367     }\r
8368     break;\r
8369   }\r
8370   return FALSE;\r
8371 }\r
8372 \r
8373 VOID\r
8374 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8375 {\r
8376     QuestionParams qp;\r
8377     FARPROC lpProc;\r
8378     \r
8379     qp.title = title;\r
8380     qp.question = question;\r
8381     qp.replyPrefix = replyPrefix;\r
8382     qp.pr = pr;\r
8383     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8384     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8385       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8386     FreeProcInstance(lpProc);\r
8387 }\r
8388 \r
8389 /* [AS] Pick FRC position */\r
8390 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8391 {\r
8392     static int * lpIndexFRC;\r
8393     BOOL index_is_ok;\r
8394     char buf[16];\r
8395 \r
8396     switch( message )\r
8397     {\r
8398     case WM_INITDIALOG:\r
8399         lpIndexFRC = (int *) lParam;\r
8400 \r
8401         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8402         Translate(hDlg, DLG_NewGameFRC);\r
8403 \r
8404         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8405         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8406         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8407         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8408 \r
8409         break;\r
8410 \r
8411     case WM_COMMAND:\r
8412         switch( LOWORD(wParam) ) {\r
8413         case IDOK:\r
8414             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8415             EndDialog( hDlg, 0 );\r
8416             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8417             return TRUE;\r
8418         case IDCANCEL:\r
8419             EndDialog( hDlg, 1 );   \r
8420             return TRUE;\r
8421         case IDC_NFG_Edit:\r
8422             if( HIWORD(wParam) == EN_CHANGE ) {\r
8423                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8424 \r
8425                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8426             }\r
8427             return TRUE;\r
8428         case IDC_NFG_Random:\r
8429           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8430             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8431             return TRUE;\r
8432         }\r
8433 \r
8434         break;\r
8435     }\r
8436 \r
8437     return FALSE;\r
8438 }\r
8439 \r
8440 int NewGameFRC()\r
8441 {\r
8442     int result;\r
8443     int index = appData.defaultFrcPosition;\r
8444     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8445 \r
8446     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8447 \r
8448     if( result == 0 ) {\r
8449         appData.defaultFrcPosition = index;\r
8450     }\r
8451 \r
8452     return result;\r
8453 }\r
8454 \r
8455 /* [AS] Game list options. Refactored by HGM */\r
8456 \r
8457 HWND gameListOptionsDialog;\r
8458 \r
8459 // low-level front-end: clear text edit / list widget\r
8460 void\r
8461 GLT_ClearList()\r
8462 {\r
8463     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8464 }\r
8465 \r
8466 // low-level front-end: clear text edit / list widget\r
8467 void\r
8468 GLT_DeSelectList()\r
8469 {\r
8470     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8471 }\r
8472 \r
8473 // low-level front-end: append line to text edit / list widget\r
8474 void\r
8475 GLT_AddToList( char *name )\r
8476 {\r
8477     if( name != 0 ) {\r
8478             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8479     }\r
8480 }\r
8481 \r
8482 // low-level front-end: get line from text edit / list widget\r
8483 Boolean\r
8484 GLT_GetFromList( int index, char *name )\r
8485 {\r
8486     if( name != 0 ) {\r
8487             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8488                 return TRUE;\r
8489     }\r
8490     return FALSE;\r
8491 }\r
8492 \r
8493 void GLT_MoveSelection( HWND hDlg, int delta )\r
8494 {\r
8495     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8496     int idx2 = idx1 + delta;\r
8497     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8498 \r
8499     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8500         char buf[128];\r
8501 \r
8502         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8503         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8504         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8505         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8506     }\r
8507 }\r
8508 \r
8509 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8510 {\r
8511     switch( message )\r
8512     {\r
8513     case WM_INITDIALOG:\r
8514         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8515         \r
8516         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8517         Translate(hDlg, DLG_GameListOptions);\r
8518 \r
8519         /* Initialize list */\r
8520         GLT_TagsToList( lpUserGLT );\r
8521 \r
8522         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8523 \r
8524         break;\r
8525 \r
8526     case WM_COMMAND:\r
8527         switch( LOWORD(wParam) ) {\r
8528         case IDOK:\r
8529             GLT_ParseList();\r
8530             EndDialog( hDlg, 0 );\r
8531             return TRUE;\r
8532         case IDCANCEL:\r
8533             EndDialog( hDlg, 1 );\r
8534             return TRUE;\r
8535 \r
8536         case IDC_GLT_Default:\r
8537             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8538             return TRUE;\r
8539 \r
8540         case IDC_GLT_Restore:\r
8541             GLT_TagsToList( appData.gameListTags );\r
8542             return TRUE;\r
8543 \r
8544         case IDC_GLT_Up:\r
8545             GLT_MoveSelection( hDlg, -1 );\r
8546             return TRUE;\r
8547 \r
8548         case IDC_GLT_Down:\r
8549             GLT_MoveSelection( hDlg, +1 );\r
8550             return TRUE;\r
8551         }\r
8552 \r
8553         break;\r
8554     }\r
8555 \r
8556     return FALSE;\r
8557 }\r
8558 \r
8559 int GameListOptions()\r
8560 {\r
8561     int result;\r
8562     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8563 \r
8564       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8565 \r
8566     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8567 \r
8568     if( result == 0 ) {\r
8569         /* [AS] Memory leak here! */\r
8570         appData.gameListTags = strdup( lpUserGLT ); \r
8571     }\r
8572 \r
8573     return result;\r
8574 }\r
8575 \r
8576 VOID\r
8577 DisplayIcsInteractionTitle(char *str)\r
8578 {\r
8579   char consoleTitle[MSG_SIZ];\r
8580 \r
8581     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8582     SetWindowText(hwndConsole, consoleTitle);\r
8583 \r
8584     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8585       char buf[MSG_SIZ], *p = buf, *q;\r
8586         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8587       do {\r
8588         q = strchr(p, ';');\r
8589         if(q) *q++ = 0;\r
8590         if(*p) ChatPopUp(p);\r
8591       } while(p=q);\r
8592     }\r
8593 \r
8594     SetActiveWindow(hwndMain);\r
8595 }\r
8596 \r
8597 void\r
8598 DrawPosition(int fullRedraw, Board board)\r
8599 {\r
8600   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8601 }\r
8602 \r
8603 void NotifyFrontendLogin()\r
8604 {\r
8605         if (hwndConsole)\r
8606                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8607 }\r
8608 \r
8609 VOID\r
8610 ResetFrontEnd()\r
8611 {\r
8612   fromX = fromY = -1;\r
8613   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8614     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8615     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8616     dragInfo.lastpos = dragInfo.pos;\r
8617     dragInfo.start.x = dragInfo.start.y = -1;\r
8618     dragInfo.from = dragInfo.start;\r
8619     ReleaseCapture();\r
8620     DrawPosition(TRUE, NULL);\r
8621   }\r
8622   TagsPopDown();\r
8623 }\r
8624 \r
8625 \r
8626 VOID\r
8627 CommentPopUp(char *title, char *str)\r
8628 {\r
8629   HWND hwnd = GetActiveWindow();\r
8630   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8631   SAY(str);\r
8632   SetActiveWindow(hwnd);\r
8633 }\r
8634 \r
8635 VOID\r
8636 CommentPopDown(void)\r
8637 {\r
8638   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8639   if (commentDialog) {\r
8640     ShowWindow(commentDialog, SW_HIDE);\r
8641   }\r
8642   commentUp = FALSE;\r
8643 }\r
8644 \r
8645 VOID\r
8646 EditCommentPopUp(int index, char *title, char *str)\r
8647 {\r
8648   EitherCommentPopUp(index, title, str, TRUE);\r
8649 }\r
8650 \r
8651 \r
8652 VOID\r
8653 RingBell()\r
8654 {\r
8655   MyPlaySound(&sounds[(int)SoundMove]);\r
8656 }\r
8657 \r
8658 VOID PlayIcsWinSound()\r
8659 {\r
8660   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8661 }\r
8662 \r
8663 VOID PlayIcsLossSound()\r
8664 {\r
8665   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8666 }\r
8667 \r
8668 VOID PlayIcsDrawSound()\r
8669 {\r
8670   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8671 }\r
8672 \r
8673 VOID PlayIcsUnfinishedSound()\r
8674 {\r
8675   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8676 }\r
8677 \r
8678 VOID\r
8679 PlayAlarmSound()\r
8680 {\r
8681   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8682 }\r
8683 \r
8684 VOID\r
8685 PlayTellSound()\r
8686 {\r
8687   MyPlaySound(&textAttribs[ColorTell].sound);\r
8688 }\r
8689 \r
8690 \r
8691 VOID\r
8692 EchoOn()\r
8693 {\r
8694   HWND hInput;\r
8695   consoleEcho = TRUE;\r
8696   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8697   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8698   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8699 }\r
8700 \r
8701 \r
8702 VOID\r
8703 EchoOff()\r
8704 {\r
8705   CHARFORMAT cf;\r
8706   HWND hInput;\r
8707   consoleEcho = FALSE;\r
8708   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8709   /* This works OK: set text and background both to the same color */\r
8710   cf = consoleCF;\r
8711   cf.crTextColor = COLOR_ECHOOFF;\r
8712   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8713   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8714 }\r
8715 \r
8716 /* No Raw()...? */\r
8717 \r
8718 void Colorize(ColorClass cc, int continuation)\r
8719 {\r
8720   currentColorClass = cc;\r
8721   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8722   consoleCF.crTextColor = textAttribs[cc].color;\r
8723   consoleCF.dwEffects = textAttribs[cc].effects;\r
8724   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8725 }\r
8726 \r
8727 char *\r
8728 UserName()\r
8729 {\r
8730   static char buf[MSG_SIZ];\r
8731   DWORD bufsiz = MSG_SIZ;\r
8732 \r
8733   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8734         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8735   }\r
8736   if (!GetUserName(buf, &bufsiz)) {\r
8737     /*DisplayError("Error getting user name", GetLastError());*/\r
8738     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8739   }\r
8740   return buf;\r
8741 }\r
8742 \r
8743 char *\r
8744 HostName()\r
8745 {\r
8746   static char buf[MSG_SIZ];\r
8747   DWORD bufsiz = MSG_SIZ;\r
8748 \r
8749   if (!GetComputerName(buf, &bufsiz)) {\r
8750     /*DisplayError("Error getting host name", GetLastError());*/\r
8751     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8752   }\r
8753   return buf;\r
8754 }\r
8755 \r
8756 \r
8757 int\r
8758 ClockTimerRunning()\r
8759 {\r
8760   return clockTimerEvent != 0;\r
8761 }\r
8762 \r
8763 int\r
8764 StopClockTimer()\r
8765 {\r
8766   if (clockTimerEvent == 0) return FALSE;\r
8767   KillTimer(hwndMain, clockTimerEvent);\r
8768   clockTimerEvent = 0;\r
8769   return TRUE;\r
8770 }\r
8771 \r
8772 void\r
8773 StartClockTimer(long millisec)\r
8774 {\r
8775   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8776                              (UINT) millisec, NULL);\r
8777 }\r
8778 \r
8779 void\r
8780 DisplayWhiteClock(long timeRemaining, int highlight)\r
8781 {\r
8782   HDC hdc;\r
8783   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8784 \r
8785   if(appData.noGUI) return;\r
8786   hdc = GetDC(hwndMain);\r
8787   if (!IsIconic(hwndMain)) {\r
8788     DisplayAClock(hdc, timeRemaining, highlight, \r
8789                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8790   }\r
8791   if (highlight && iconCurrent == iconBlack) {\r
8792     iconCurrent = iconWhite;\r
8793     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8794     if (IsIconic(hwndMain)) {\r
8795       DrawIcon(hdc, 2, 2, iconCurrent);\r
8796     }\r
8797   }\r
8798   (void) ReleaseDC(hwndMain, hdc);\r
8799   if (hwndConsole)\r
8800     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8801 }\r
8802 \r
8803 void\r
8804 DisplayBlackClock(long timeRemaining, int highlight)\r
8805 {\r
8806   HDC hdc;\r
8807   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8808 \r
8809   if(appData.noGUI) return;\r
8810   hdc = GetDC(hwndMain);\r
8811   if (!IsIconic(hwndMain)) {\r
8812     DisplayAClock(hdc, timeRemaining, highlight, \r
8813                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8814   }\r
8815   if (highlight && iconCurrent == iconWhite) {\r
8816     iconCurrent = iconBlack;\r
8817     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8818     if (IsIconic(hwndMain)) {\r
8819       DrawIcon(hdc, 2, 2, iconCurrent);\r
8820     }\r
8821   }\r
8822   (void) ReleaseDC(hwndMain, hdc);\r
8823   if (hwndConsole)\r
8824     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8825 }\r
8826 \r
8827 \r
8828 int\r
8829 LoadGameTimerRunning()\r
8830 {\r
8831   return loadGameTimerEvent != 0;\r
8832 }\r
8833 \r
8834 int\r
8835 StopLoadGameTimer()\r
8836 {\r
8837   if (loadGameTimerEvent == 0) return FALSE;\r
8838   KillTimer(hwndMain, loadGameTimerEvent);\r
8839   loadGameTimerEvent = 0;\r
8840   return TRUE;\r
8841 }\r
8842 \r
8843 void\r
8844 StartLoadGameTimer(long millisec)\r
8845 {\r
8846   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8847                                 (UINT) millisec, NULL);\r
8848 }\r
8849 \r
8850 void\r
8851 AutoSaveGame()\r
8852 {\r
8853   char *defName;\r
8854   FILE *f;\r
8855   char fileTitle[MSG_SIZ];\r
8856 \r
8857   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8858   f = OpenFileDialog(hwndMain, "a", defName,\r
8859                      appData.oldSaveStyle ? "gam" : "pgn",\r
8860                      GAME_FILT, \r
8861                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8862   if (f != NULL) {\r
8863     SaveGame(f, 0, "");\r
8864     fclose(f);\r
8865   }\r
8866 }\r
8867 \r
8868 \r
8869 void\r
8870 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8871 {\r
8872   if (delayedTimerEvent != 0) {\r
8873     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8874       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8875     }\r
8876     KillTimer(hwndMain, delayedTimerEvent);\r
8877     delayedTimerEvent = 0;\r
8878     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8879     delayedTimerCallback();\r
8880   }\r
8881   delayedTimerCallback = cb;\r
8882   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8883                                 (UINT) millisec, NULL);\r
8884 }\r
8885 \r
8886 DelayedEventCallback\r
8887 GetDelayedEvent()\r
8888 {\r
8889   if (delayedTimerEvent) {\r
8890     return delayedTimerCallback;\r
8891   } else {\r
8892     return NULL;\r
8893   }\r
8894 }\r
8895 \r
8896 void\r
8897 CancelDelayedEvent()\r
8898 {\r
8899   if (delayedTimerEvent) {\r
8900     KillTimer(hwndMain, delayedTimerEvent);\r
8901     delayedTimerEvent = 0;\r
8902   }\r
8903 }\r
8904 \r
8905 DWORD GetWin32Priority(int nice)\r
8906 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8907 /*\r
8908 REALTIME_PRIORITY_CLASS     0x00000100\r
8909 HIGH_PRIORITY_CLASS         0x00000080\r
8910 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8911 NORMAL_PRIORITY_CLASS       0x00000020\r
8912 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8913 IDLE_PRIORITY_CLASS         0x00000040\r
8914 */\r
8915         if (nice < -15) return 0x00000080;\r
8916         if (nice < 0)   return 0x00008000;\r
8917         if (nice == 0)  return 0x00000020;\r
8918         if (nice < 15)  return 0x00004000;\r
8919         return 0x00000040;\r
8920 }\r
8921 \r
8922 void RunCommand(char *cmdLine)\r
8923 {\r
8924   /* Now create the child process. */\r
8925   STARTUPINFO siStartInfo;\r
8926   PROCESS_INFORMATION piProcInfo;\r
8927 \r
8928   siStartInfo.cb = sizeof(STARTUPINFO);\r
8929   siStartInfo.lpReserved = NULL;\r
8930   siStartInfo.lpDesktop = NULL;\r
8931   siStartInfo.lpTitle = NULL;\r
8932   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8933   siStartInfo.cbReserved2 = 0;\r
8934   siStartInfo.lpReserved2 = NULL;\r
8935   siStartInfo.hStdInput = NULL;\r
8936   siStartInfo.hStdOutput = NULL;\r
8937   siStartInfo.hStdError = NULL;\r
8938 \r
8939   CreateProcess(NULL,\r
8940                 cmdLine,           /* command line */\r
8941                 NULL,      /* process security attributes */\r
8942                 NULL,      /* primary thread security attrs */\r
8943                 TRUE,      /* handles are inherited */\r
8944                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8945                 NULL,      /* use parent's environment */\r
8946                 NULL,\r
8947                 &siStartInfo, /* STARTUPINFO pointer */\r
8948                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8949 \r
8950   CloseHandle(piProcInfo.hThread);\r
8951 }\r
8952 \r
8953 /* Start a child process running the given program.\r
8954    The process's standard output can be read from "from", and its\r
8955    standard input can be written to "to".\r
8956    Exit with fatal error if anything goes wrong.\r
8957    Returns an opaque pointer that can be used to destroy the process\r
8958    later.\r
8959 */\r
8960 int\r
8961 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8962 {\r
8963 #define BUFSIZE 4096\r
8964 \r
8965   HANDLE hChildStdinRd, hChildStdinWr,\r
8966     hChildStdoutRd, hChildStdoutWr;\r
8967   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8968   SECURITY_ATTRIBUTES saAttr;\r
8969   BOOL fSuccess;\r
8970   PROCESS_INFORMATION piProcInfo;\r
8971   STARTUPINFO siStartInfo;\r
8972   ChildProc *cp;\r
8973   char buf[MSG_SIZ];\r
8974   DWORD err;\r
8975 \r
8976   if (appData.debugMode) {\r
8977     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8978   }\r
8979 \r
8980   *pr = NoProc;\r
8981 \r
8982   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8983   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8984   saAttr.bInheritHandle = TRUE;\r
8985   saAttr.lpSecurityDescriptor = NULL;\r
8986 \r
8987   /*\r
8988    * The steps for redirecting child's STDOUT:\r
8989    *     1. Create anonymous pipe to be STDOUT for child.\r
8990    *     2. Create a noninheritable duplicate of read handle,\r
8991    *         and close the inheritable read handle.\r
8992    */\r
8993 \r
8994   /* Create a pipe for the child's STDOUT. */\r
8995   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8996     return GetLastError();\r
8997   }\r
8998 \r
8999   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9000   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9001                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9002                              FALSE,     /* not inherited */\r
9003                              DUPLICATE_SAME_ACCESS);\r
9004   if (! fSuccess) {\r
9005     return GetLastError();\r
9006   }\r
9007   CloseHandle(hChildStdoutRd);\r
9008 \r
9009   /*\r
9010    * The steps for redirecting child's STDIN:\r
9011    *     1. Create anonymous pipe to be STDIN for child.\r
9012    *     2. Create a noninheritable duplicate of write handle,\r
9013    *         and close the inheritable write handle.\r
9014    */\r
9015 \r
9016   /* Create a pipe for the child's STDIN. */\r
9017   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9018     return GetLastError();\r
9019   }\r
9020 \r
9021   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9022   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9023                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9024                              FALSE,     /* not inherited */\r
9025                              DUPLICATE_SAME_ACCESS);\r
9026   if (! fSuccess) {\r
9027     return GetLastError();\r
9028   }\r
9029   CloseHandle(hChildStdinWr);\r
9030 \r
9031   /* Arrange to (1) look in dir for the child .exe file, and\r
9032    * (2) have dir be the child's working directory.  Interpret\r
9033    * dir relative to the directory WinBoard loaded from. */\r
9034   GetCurrentDirectory(MSG_SIZ, buf);\r
9035   SetCurrentDirectory(installDir);\r
9036   SetCurrentDirectory(dir);\r
9037 \r
9038   /* Now create the child process. */\r
9039 \r
9040   siStartInfo.cb = sizeof(STARTUPINFO);\r
9041   siStartInfo.lpReserved = NULL;\r
9042   siStartInfo.lpDesktop = NULL;\r
9043   siStartInfo.lpTitle = NULL;\r
9044   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9045   siStartInfo.cbReserved2 = 0;\r
9046   siStartInfo.lpReserved2 = NULL;\r
9047   siStartInfo.hStdInput = hChildStdinRd;\r
9048   siStartInfo.hStdOutput = hChildStdoutWr;\r
9049   siStartInfo.hStdError = hChildStdoutWr;\r
9050 \r
9051   fSuccess = CreateProcess(NULL,\r
9052                            cmdLine,        /* command line */\r
9053                            NULL,           /* process security attributes */\r
9054                            NULL,           /* primary thread security attrs */\r
9055                            TRUE,           /* handles are inherited */\r
9056                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9057                            NULL,           /* use parent's environment */\r
9058                            NULL,\r
9059                            &siStartInfo, /* STARTUPINFO pointer */\r
9060                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9061 \r
9062   err = GetLastError();\r
9063   SetCurrentDirectory(buf); /* return to prev directory */\r
9064   if (! fSuccess) {\r
9065     return err;\r
9066   }\r
9067 \r
9068   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9069     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9070     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9071   }\r
9072 \r
9073   /* Close the handles we don't need in the parent */\r
9074   CloseHandle(piProcInfo.hThread);\r
9075   CloseHandle(hChildStdinRd);\r
9076   CloseHandle(hChildStdoutWr);\r
9077 \r
9078   /* Prepare return value */\r
9079   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9080   cp->kind = CPReal;\r
9081   cp->hProcess = piProcInfo.hProcess;\r
9082   cp->pid = piProcInfo.dwProcessId;\r
9083   cp->hFrom = hChildStdoutRdDup;\r
9084   cp->hTo = hChildStdinWrDup;\r
9085 \r
9086   *pr = (void *) cp;\r
9087 \r
9088   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9089      2000 where engines sometimes don't see the initial command(s)\r
9090      from WinBoard and hang.  I don't understand how that can happen,\r
9091      but the Sleep is harmless, so I've put it in.  Others have also\r
9092      reported what may be the same problem, so hopefully this will fix\r
9093      it for them too.  */\r
9094   Sleep(500);\r
9095 \r
9096   return NO_ERROR;\r
9097 }\r
9098 \r
9099 \r
9100 void\r
9101 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9102 {\r
9103   ChildProc *cp; int result;\r
9104 \r
9105   cp = (ChildProc *) pr;\r
9106   if (cp == NULL) return;\r
9107 \r
9108   switch (cp->kind) {\r
9109   case CPReal:\r
9110     /* TerminateProcess is considered harmful, so... */\r
9111     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9112     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9113     /* The following doesn't work because the chess program\r
9114        doesn't "have the same console" as WinBoard.  Maybe\r
9115        we could arrange for this even though neither WinBoard\r
9116        nor the chess program uses a console for stdio? */\r
9117     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9118 \r
9119     /* [AS] Special termination modes for misbehaving programs... */\r
9120     if( signal == 9 ) { \r
9121         result = TerminateProcess( cp->hProcess, 0 );\r
9122 \r
9123         if ( appData.debugMode) {\r
9124             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9125         }\r
9126     }\r
9127     else if( signal == 10 ) {\r
9128         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9129 \r
9130         if( dw != WAIT_OBJECT_0 ) {\r
9131             result = TerminateProcess( cp->hProcess, 0 );\r
9132 \r
9133             if ( appData.debugMode) {\r
9134                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9135             }\r
9136 \r
9137         }\r
9138     }\r
9139 \r
9140     CloseHandle(cp->hProcess);\r
9141     break;\r
9142 \r
9143   case CPComm:\r
9144     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9145     break;\r
9146 \r
9147   case CPSock:\r
9148     closesocket(cp->sock);\r
9149     WSACleanup();\r
9150     break;\r
9151 \r
9152   case CPRcmd:\r
9153     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9154     closesocket(cp->sock);\r
9155     closesocket(cp->sock2);\r
9156     WSACleanup();\r
9157     break;\r
9158   }\r
9159   free(cp);\r
9160 }\r
9161 \r
9162 void\r
9163 InterruptChildProcess(ProcRef pr)\r
9164 {\r
9165   ChildProc *cp;\r
9166 \r
9167   cp = (ChildProc *) pr;\r
9168   if (cp == NULL) return;\r
9169   switch (cp->kind) {\r
9170   case CPReal:\r
9171     /* The following doesn't work because the chess program\r
9172        doesn't "have the same console" as WinBoard.  Maybe\r
9173        we could arrange for this even though neither WinBoard\r
9174        nor the chess program uses a console for stdio */\r
9175     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9176     break;\r
9177 \r
9178   case CPComm:\r
9179   case CPSock:\r
9180     /* Can't interrupt */\r
9181     break;\r
9182 \r
9183   case CPRcmd:\r
9184     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9185     break;\r
9186   }\r
9187 }\r
9188 \r
9189 \r
9190 int\r
9191 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9192 {\r
9193   char cmdLine[MSG_SIZ];\r
9194 \r
9195   if (port[0] == NULLCHAR) {\r
9196     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9197   } else {\r
9198     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9199   }\r
9200   return StartChildProcess(cmdLine, "", pr);\r
9201 }\r
9202 \r
9203 \r
9204 /* Code to open TCP sockets */\r
9205 \r
9206 int\r
9207 OpenTCP(char *host, char *port, ProcRef *pr)\r
9208 {\r
9209   ChildProc *cp;\r
9210   int err;\r
9211   SOCKET s;\r
9212 \r
9213   struct sockaddr_in sa, mysa;\r
9214   struct hostent FAR *hp;\r
9215   unsigned short uport;\r
9216   WORD wVersionRequested;\r
9217   WSADATA wsaData;\r
9218 \r
9219   /* Initialize socket DLL */\r
9220   wVersionRequested = MAKEWORD(1, 1);\r
9221   err = WSAStartup(wVersionRequested, &wsaData);\r
9222   if (err != 0) return err;\r
9223 \r
9224   /* Make socket */\r
9225   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9226     err = WSAGetLastError();\r
9227     WSACleanup();\r
9228     return err;\r
9229   }\r
9230 \r
9231   /* Bind local address using (mostly) don't-care values.\r
9232    */\r
9233   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9234   mysa.sin_family = AF_INET;\r
9235   mysa.sin_addr.s_addr = INADDR_ANY;\r
9236   uport = (unsigned short) 0;\r
9237   mysa.sin_port = htons(uport);\r
9238   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9239       == SOCKET_ERROR) {\r
9240     err = WSAGetLastError();\r
9241     WSACleanup();\r
9242     return err;\r
9243   }\r
9244 \r
9245   /* Resolve remote host name */\r
9246   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9247   if (!(hp = gethostbyname(host))) {\r
9248     unsigned int b0, b1, b2, b3;\r
9249 \r
9250     err = WSAGetLastError();\r
9251 \r
9252     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9253       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9254       hp->h_addrtype = AF_INET;\r
9255       hp->h_length = 4;\r
9256       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9257       hp->h_addr_list[0] = (char *) malloc(4);\r
9258       hp->h_addr_list[0][0] = (char) b0;\r
9259       hp->h_addr_list[0][1] = (char) b1;\r
9260       hp->h_addr_list[0][2] = (char) b2;\r
9261       hp->h_addr_list[0][3] = (char) b3;\r
9262     } else {\r
9263       WSACleanup();\r
9264       return err;\r
9265     }\r
9266   }\r
9267   sa.sin_family = hp->h_addrtype;\r
9268   uport = (unsigned short) atoi(port);\r
9269   sa.sin_port = htons(uport);\r
9270   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9271 \r
9272   /* Make connection */\r
9273   if (connect(s, (struct sockaddr *) &sa,\r
9274               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9275     err = WSAGetLastError();\r
9276     WSACleanup();\r
9277     return err;\r
9278   }\r
9279 \r
9280   /* Prepare return value */\r
9281   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9282   cp->kind = CPSock;\r
9283   cp->sock = s;\r
9284   *pr = (ProcRef *) cp;\r
9285 \r
9286   return NO_ERROR;\r
9287 }\r
9288 \r
9289 int\r
9290 OpenCommPort(char *name, ProcRef *pr)\r
9291 {\r
9292   HANDLE h;\r
9293   COMMTIMEOUTS ct;\r
9294   ChildProc *cp;\r
9295   char fullname[MSG_SIZ];\r
9296 \r
9297   if (*name != '\\')\r
9298     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9299   else\r
9300     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9301 \r
9302   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9303                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9304   if (h == (HANDLE) -1) {\r
9305     return GetLastError();\r
9306   }\r
9307   hCommPort = h;\r
9308 \r
9309   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9310 \r
9311   /* Accumulate characters until a 100ms pause, then parse */\r
9312   ct.ReadIntervalTimeout = 100;\r
9313   ct.ReadTotalTimeoutMultiplier = 0;\r
9314   ct.ReadTotalTimeoutConstant = 0;\r
9315   ct.WriteTotalTimeoutMultiplier = 0;\r
9316   ct.WriteTotalTimeoutConstant = 0;\r
9317   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9318 \r
9319   /* Prepare return value */\r
9320   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9321   cp->kind = CPComm;\r
9322   cp->hFrom = h;\r
9323   cp->hTo = h;\r
9324   *pr = (ProcRef *) cp;\r
9325 \r
9326   return NO_ERROR;\r
9327 }\r
9328 \r
9329 int\r
9330 OpenLoopback(ProcRef *pr)\r
9331 {\r
9332   DisplayFatalError(_("Not implemented"), 0, 1);\r
9333   return NO_ERROR;\r
9334 }\r
9335 \r
9336 \r
9337 int\r
9338 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9339 {\r
9340   ChildProc *cp;\r
9341   int err;\r
9342   SOCKET s, s2, s3;\r
9343   struct sockaddr_in sa, mysa;\r
9344   struct hostent FAR *hp;\r
9345   unsigned short uport;\r
9346   WORD wVersionRequested;\r
9347   WSADATA wsaData;\r
9348   int fromPort;\r
9349   char stderrPortStr[MSG_SIZ];\r
9350 \r
9351   /* Initialize socket DLL */\r
9352   wVersionRequested = MAKEWORD(1, 1);\r
9353   err = WSAStartup(wVersionRequested, &wsaData);\r
9354   if (err != 0) return err;\r
9355 \r
9356   /* Resolve remote host name */\r
9357   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9358   if (!(hp = gethostbyname(host))) {\r
9359     unsigned int b0, b1, b2, b3;\r
9360 \r
9361     err = WSAGetLastError();\r
9362 \r
9363     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9364       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9365       hp->h_addrtype = AF_INET;\r
9366       hp->h_length = 4;\r
9367       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9368       hp->h_addr_list[0] = (char *) malloc(4);\r
9369       hp->h_addr_list[0][0] = (char) b0;\r
9370       hp->h_addr_list[0][1] = (char) b1;\r
9371       hp->h_addr_list[0][2] = (char) b2;\r
9372       hp->h_addr_list[0][3] = (char) b3;\r
9373     } else {\r
9374       WSACleanup();\r
9375       return err;\r
9376     }\r
9377   }\r
9378   sa.sin_family = hp->h_addrtype;\r
9379   uport = (unsigned short) 514;\r
9380   sa.sin_port = htons(uport);\r
9381   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9382 \r
9383   /* Bind local socket to unused "privileged" port address\r
9384    */\r
9385   s = INVALID_SOCKET;\r
9386   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9387   mysa.sin_family = AF_INET;\r
9388   mysa.sin_addr.s_addr = INADDR_ANY;\r
9389   for (fromPort = 1023;; fromPort--) {\r
9390     if (fromPort < 0) {\r
9391       WSACleanup();\r
9392       return WSAEADDRINUSE;\r
9393     }\r
9394     if (s == INVALID_SOCKET) {\r
9395       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9396         err = WSAGetLastError();\r
9397         WSACleanup();\r
9398         return err;\r
9399       }\r
9400     }\r
9401     uport = (unsigned short) fromPort;\r
9402     mysa.sin_port = htons(uport);\r
9403     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9404         == SOCKET_ERROR) {\r
9405       err = WSAGetLastError();\r
9406       if (err == WSAEADDRINUSE) continue;\r
9407       WSACleanup();\r
9408       return err;\r
9409     }\r
9410     if (connect(s, (struct sockaddr *) &sa,\r
9411       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9412       err = WSAGetLastError();\r
9413       if (err == WSAEADDRINUSE) {\r
9414         closesocket(s);\r
9415         s = -1;\r
9416         continue;\r
9417       }\r
9418       WSACleanup();\r
9419       return err;\r
9420     }\r
9421     break;\r
9422   }\r
9423 \r
9424   /* Bind stderr local socket to unused "privileged" port address\r
9425    */\r
9426   s2 = INVALID_SOCKET;\r
9427   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9428   mysa.sin_family = AF_INET;\r
9429   mysa.sin_addr.s_addr = INADDR_ANY;\r
9430   for (fromPort = 1023;; fromPort--) {\r
9431     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9432     if (fromPort < 0) {\r
9433       (void) closesocket(s);\r
9434       WSACleanup();\r
9435       return WSAEADDRINUSE;\r
9436     }\r
9437     if (s2 == INVALID_SOCKET) {\r
9438       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9439         err = WSAGetLastError();\r
9440         closesocket(s);\r
9441         WSACleanup();\r
9442         return err;\r
9443       }\r
9444     }\r
9445     uport = (unsigned short) fromPort;\r
9446     mysa.sin_port = htons(uport);\r
9447     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9448         == SOCKET_ERROR) {\r
9449       err = WSAGetLastError();\r
9450       if (err == WSAEADDRINUSE) continue;\r
9451       (void) closesocket(s);\r
9452       WSACleanup();\r
9453       return err;\r
9454     }\r
9455     if (listen(s2, 1) == SOCKET_ERROR) {\r
9456       err = WSAGetLastError();\r
9457       if (err == WSAEADDRINUSE) {\r
9458         closesocket(s2);\r
9459         s2 = INVALID_SOCKET;\r
9460         continue;\r
9461       }\r
9462       (void) closesocket(s);\r
9463       (void) closesocket(s2);\r
9464       WSACleanup();\r
9465       return err;\r
9466     }\r
9467     break;\r
9468   }\r
9469   prevStderrPort = fromPort; // remember port used\r
9470   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9471 \r
9472   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9473     err = WSAGetLastError();\r
9474     (void) closesocket(s);\r
9475     (void) closesocket(s2);\r
9476     WSACleanup();\r
9477     return err;\r
9478   }\r
9479 \r
9480   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9481     err = WSAGetLastError();\r
9482     (void) closesocket(s);\r
9483     (void) closesocket(s2);\r
9484     WSACleanup();\r
9485     return err;\r
9486   }\r
9487   if (*user == NULLCHAR) user = UserName();\r
9488   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9489     err = WSAGetLastError();\r
9490     (void) closesocket(s);\r
9491     (void) closesocket(s2);\r
9492     WSACleanup();\r
9493     return err;\r
9494   }\r
9495   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9496     err = WSAGetLastError();\r
9497     (void) closesocket(s);\r
9498     (void) closesocket(s2);\r
9499     WSACleanup();\r
9500     return err;\r
9501   }\r
9502 \r
9503   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9504     err = WSAGetLastError();\r
9505     (void) closesocket(s);\r
9506     (void) closesocket(s2);\r
9507     WSACleanup();\r
9508     return err;\r
9509   }\r
9510   (void) closesocket(s2);  /* Stop listening */\r
9511 \r
9512   /* Prepare return value */\r
9513   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9514   cp->kind = CPRcmd;\r
9515   cp->sock = s;\r
9516   cp->sock2 = s3;\r
9517   *pr = (ProcRef *) cp;\r
9518 \r
9519   return NO_ERROR;\r
9520 }\r
9521 \r
9522 \r
9523 InputSourceRef\r
9524 AddInputSource(ProcRef pr, int lineByLine,\r
9525                InputCallback func, VOIDSTAR closure)\r
9526 {\r
9527   InputSource *is, *is2 = NULL;\r
9528   ChildProc *cp = (ChildProc *) pr;\r
9529 \r
9530   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9531   is->lineByLine = lineByLine;\r
9532   is->func = func;\r
9533   is->closure = closure;\r
9534   is->second = NULL;\r
9535   is->next = is->buf;\r
9536   if (pr == NoProc) {\r
9537     is->kind = CPReal;\r
9538     consoleInputSource = is;\r
9539   } else {\r
9540     is->kind = cp->kind;\r
9541     /* \r
9542         [AS] Try to avoid a race condition if the thread is given control too early:\r
9543         we create all threads suspended so that the is->hThread variable can be\r
9544         safely assigned, then let the threads start with ResumeThread.\r
9545     */\r
9546     switch (cp->kind) {\r
9547     case CPReal:\r
9548       is->hFile = cp->hFrom;\r
9549       cp->hFrom = NULL; /* now owned by InputThread */\r
9550       is->hThread =\r
9551         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9552                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9553       break;\r
9554 \r
9555     case CPComm:\r
9556       is->hFile = cp->hFrom;\r
9557       cp->hFrom = NULL; /* now owned by InputThread */\r
9558       is->hThread =\r
9559         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9560                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9561       break;\r
9562 \r
9563     case CPSock:\r
9564       is->sock = cp->sock;\r
9565       is->hThread =\r
9566         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9567                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9568       break;\r
9569 \r
9570     case CPRcmd:\r
9571       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9572       *is2 = *is;\r
9573       is->sock = cp->sock;\r
9574       is->second = is2;\r
9575       is2->sock = cp->sock2;\r
9576       is2->second = is2;\r
9577       is->hThread =\r
9578         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9579                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9580       is2->hThread =\r
9581         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9582                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9583       break;\r
9584     }\r
9585 \r
9586     if( is->hThread != NULL ) {\r
9587         ResumeThread( is->hThread );\r
9588     }\r
9589 \r
9590     if( is2 != NULL && is2->hThread != NULL ) {\r
9591         ResumeThread( is2->hThread );\r
9592     }\r
9593   }\r
9594 \r
9595   return (InputSourceRef) is;\r
9596 }\r
9597 \r
9598 void\r
9599 RemoveInputSource(InputSourceRef isr)\r
9600 {\r
9601   InputSource *is;\r
9602 \r
9603   is = (InputSource *) isr;\r
9604   is->hThread = NULL;  /* tell thread to stop */\r
9605   CloseHandle(is->hThread);\r
9606   if (is->second != NULL) {\r
9607     is->second->hThread = NULL;\r
9608     CloseHandle(is->second->hThread);\r
9609   }\r
9610 }\r
9611 \r
9612 int no_wrap(char *message, int count)\r
9613 {\r
9614     ConsoleOutput(message, count, FALSE);\r
9615     return count;\r
9616 }\r
9617 \r
9618 int\r
9619 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9620 {\r
9621   DWORD dOutCount;\r
9622   int outCount = SOCKET_ERROR;\r
9623   ChildProc *cp = (ChildProc *) pr;\r
9624   static OVERLAPPED ovl;\r
9625   static int line = 0;\r
9626 \r
9627   if (pr == NoProc)\r
9628   {\r
9629     if (appData.noJoin || !appData.useInternalWrap)\r
9630       return no_wrap(message, count);\r
9631     else\r
9632     {\r
9633       int width = get_term_width();\r
9634       int len = wrap(NULL, message, count, width, &line);\r
9635       char *msg = malloc(len);\r
9636       int dbgchk;\r
9637 \r
9638       if (!msg)\r
9639         return no_wrap(message, count);\r
9640       else\r
9641       {\r
9642         dbgchk = wrap(msg, message, count, width, &line);\r
9643         if (dbgchk != len && appData.debugMode)\r
9644             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9645         ConsoleOutput(msg, len, FALSE);\r
9646         free(msg);\r
9647         return len;\r
9648       }\r
9649     }\r
9650   }\r
9651 \r
9652   if (ovl.hEvent == NULL) {\r
9653     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9654   }\r
9655   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9656 \r
9657   switch (cp->kind) {\r
9658   case CPSock:\r
9659   case CPRcmd:\r
9660     outCount = send(cp->sock, message, count, 0);\r
9661     if (outCount == SOCKET_ERROR) {\r
9662       *outError = WSAGetLastError();\r
9663     } else {\r
9664       *outError = NO_ERROR;\r
9665     }\r
9666     break;\r
9667 \r
9668   case CPReal:\r
9669     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9670                   &dOutCount, NULL)) {\r
9671       *outError = NO_ERROR;\r
9672       outCount = (int) dOutCount;\r
9673     } else {\r
9674       *outError = GetLastError();\r
9675     }\r
9676     break;\r
9677 \r
9678   case CPComm:\r
9679     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9680                             &dOutCount, &ovl);\r
9681     if (*outError == NO_ERROR) {\r
9682       outCount = (int) dOutCount;\r
9683     }\r
9684     break;\r
9685   }\r
9686   return outCount;\r
9687 }\r
9688 \r
9689 void\r
9690 DoSleep(int n)\r
9691 {\r
9692     if(n != 0) Sleep(n);\r
9693 }\r
9694 \r
9695 int\r
9696 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9697                        long msdelay)\r
9698 {\r
9699   /* Ignore delay, not implemented for WinBoard */\r
9700   return OutputToProcess(pr, message, count, outError);\r
9701 }\r
9702 \r
9703 \r
9704 void\r
9705 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9706                         char *buf, int count, int error)\r
9707 {\r
9708   DisplayFatalError(_("Not implemented"), 0, 1);\r
9709 }\r
9710 \r
9711 /* see wgamelist.c for Game List functions */\r
9712 /* see wedittags.c for Edit Tags functions */\r
9713 \r
9714 \r
9715 VOID\r
9716 ICSInitScript()\r
9717 {\r
9718   FILE *f;\r
9719   char buf[MSG_SIZ];\r
9720   char *dummy;\r
9721 \r
9722   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9723     f = fopen(buf, "r");\r
9724     if (f != NULL) {\r
9725       ProcessICSInitScript(f);\r
9726       fclose(f);\r
9727     }\r
9728   }\r
9729 }\r
9730 \r
9731 \r
9732 VOID\r
9733 StartAnalysisClock()\r
9734 {\r
9735   if (analysisTimerEvent) return;\r
9736   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9737                                         (UINT) 2000, NULL);\r
9738 }\r
9739 \r
9740 VOID\r
9741 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9742 {\r
9743   highlightInfo.sq[0].x = fromX;\r
9744   highlightInfo.sq[0].y = fromY;\r
9745   highlightInfo.sq[1].x = toX;\r
9746   highlightInfo.sq[1].y = toY;\r
9747 }\r
9748 \r
9749 VOID\r
9750 ClearHighlights()\r
9751 {\r
9752   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9753     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9754 }\r
9755 \r
9756 VOID\r
9757 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9758 {\r
9759   premoveHighlightInfo.sq[0].x = fromX;\r
9760   premoveHighlightInfo.sq[0].y = fromY;\r
9761   premoveHighlightInfo.sq[1].x = toX;\r
9762   premoveHighlightInfo.sq[1].y = toY;\r
9763 }\r
9764 \r
9765 VOID\r
9766 ClearPremoveHighlights()\r
9767 {\r
9768   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9769     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9770 }\r
9771 \r
9772 VOID\r
9773 ShutDownFrontEnd()\r
9774 {\r
9775   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9776   DeleteClipboardTempFiles();\r
9777 }\r
9778 \r
9779 void\r
9780 BoardToTop()\r
9781 {\r
9782     if (IsIconic(hwndMain))\r
9783       ShowWindow(hwndMain, SW_RESTORE);\r
9784 \r
9785     SetActiveWindow(hwndMain);\r
9786 }\r
9787 \r
9788 /*\r
9789  * Prototypes for animation support routines\r
9790  */\r
9791 static void ScreenSquare(int column, int row, POINT * pt);\r
9792 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9793      POINT frames[], int * nFrames);\r
9794 \r
9795 \r
9796 #define kFactor 4\r
9797 \r
9798 void\r
9799 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9800 {       // [HGM] atomic: animate blast wave\r
9801         int i;\r
9802 \r
9803         explodeInfo.fromX = fromX;\r
9804         explodeInfo.fromY = fromY;\r
9805         explodeInfo.toX = toX;\r
9806         explodeInfo.toY = toY;\r
9807         for(i=1; i<4*kFactor; i++) {\r
9808             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9809             DrawPosition(FALSE, board);\r
9810             Sleep(appData.animSpeed);\r
9811         }\r
9812         explodeInfo.radius = 0;\r
9813         DrawPosition(TRUE, board);\r
9814 }\r
9815 \r
9816 void\r
9817 AnimateMove(board, fromX, fromY, toX, toY)\r
9818      Board board;\r
9819      int fromX;\r
9820      int fromY;\r
9821      int toX;\r
9822      int toY;\r
9823 {\r
9824   ChessSquare piece;\r
9825   POINT start, finish, mid;\r
9826   POINT frames[kFactor * 2 + 1];\r
9827   int nFrames, n;\r
9828 \r
9829   if (!appData.animate) return;\r
9830   if (doingSizing) return;\r
9831   if (fromY < 0 || fromX < 0) return;\r
9832   piece = board[fromY][fromX];\r
9833   if (piece >= EmptySquare) return;\r
9834 \r
9835   ScreenSquare(fromX, fromY, &start);\r
9836   ScreenSquare(toX, toY, &finish);\r
9837 \r
9838   /* All moves except knight jumps move in straight line */\r
9839   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9840     mid.x = start.x + (finish.x - start.x) / 2;\r
9841     mid.y = start.y + (finish.y - start.y) / 2;\r
9842   } else {\r
9843     /* Knight: make straight movement then diagonal */\r
9844     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9845        mid.x = start.x + (finish.x - start.x) / 2;\r
9846        mid.y = start.y;\r
9847      } else {\r
9848        mid.x = start.x;\r
9849        mid.y = start.y + (finish.y - start.y) / 2;\r
9850      }\r
9851   }\r
9852   \r
9853   /* Don't use as many frames for very short moves */\r
9854   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9855     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9856   else\r
9857     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9858 \r
9859   animInfo.from.x = fromX;\r
9860   animInfo.from.y = fromY;\r
9861   animInfo.to.x = toX;\r
9862   animInfo.to.y = toY;\r
9863   animInfo.lastpos = start;\r
9864   animInfo.piece = piece;\r
9865   for (n = 0; n < nFrames; n++) {\r
9866     animInfo.pos = frames[n];\r
9867     DrawPosition(FALSE, NULL);\r
9868     animInfo.lastpos = animInfo.pos;\r
9869     Sleep(appData.animSpeed);\r
9870   }\r
9871   animInfo.pos = finish;\r
9872   DrawPosition(FALSE, NULL);\r
9873   animInfo.piece = EmptySquare;\r
9874   Explode(board, fromX, fromY, toX, toY);\r
9875 }\r
9876 \r
9877 /*      Convert board position to corner of screen rect and color       */\r
9878 \r
9879 static void\r
9880 ScreenSquare(column, row, pt)\r
9881      int column; int row; POINT * pt;\r
9882 {\r
9883   if (flipView) {\r
9884     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9885     pt->y = lineGap + row * (squareSize + lineGap);\r
9886   } else {\r
9887     pt->x = lineGap + column * (squareSize + lineGap);\r
9888     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9889   }\r
9890 }\r
9891 \r
9892 /*      Generate a series of frame coords from start->mid->finish.\r
9893         The movement rate doubles until the half way point is\r
9894         reached, then halves back down to the final destination,\r
9895         which gives a nice slow in/out effect. The algorithmn\r
9896         may seem to generate too many intermediates for short\r
9897         moves, but remember that the purpose is to attract the\r
9898         viewers attention to the piece about to be moved and\r
9899         then to where it ends up. Too few frames would be less\r
9900         noticeable.                                             */\r
9901 \r
9902 static void\r
9903 Tween(start, mid, finish, factor, frames, nFrames)\r
9904      POINT * start; POINT * mid;\r
9905      POINT * finish; int factor;\r
9906      POINT frames[]; int * nFrames;\r
9907 {\r
9908   int n, fraction = 1, count = 0;\r
9909 \r
9910   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9911   for (n = 0; n < factor; n++)\r
9912     fraction *= 2;\r
9913   for (n = 0; n < factor; n++) {\r
9914     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9915     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9916     count ++;\r
9917     fraction = fraction / 2;\r
9918   }\r
9919   \r
9920   /* Midpoint */\r
9921   frames[count] = *mid;\r
9922   count ++;\r
9923   \r
9924   /* Slow out, stepping 1/2, then 1/4, ... */\r
9925   fraction = 2;\r
9926   for (n = 0; n < factor; n++) {\r
9927     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9928     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9929     count ++;\r
9930     fraction = fraction * 2;\r
9931   }\r
9932   *nFrames = count;\r
9933 }\r
9934 \r
9935 void\r
9936 SettingsPopUp(ChessProgramState *cps)\r
9937 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9938       EngineOptionsPopup(savedHwnd, cps);\r
9939 }\r
9940 \r
9941 int flock(int fid, int code)\r
9942 {\r
9943     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9944     OVERLAPPED ov;\r
9945     ov.hEvent = NULL;\r
9946     ov.Offset = 0;\r
9947     ov.OffsetHigh = 0;\r
9948     switch(code) {\r
9949       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9950       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9951       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9952       default: return -1;\r
9953     }\r
9954     return 0;\r
9955 }\r