88092ac45e4f80486a8feae32ff606f5defcf513
[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 enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { 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
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         char *opponent = "";\r
1010         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1011         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1012         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1013         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1014             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1015             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1016         }\r
1017       } else\r
1018       if(appData.directory[n] && appData.directory[n][0]) {\r
1019         SetCurrentDirectory(appData.directory[n]);\r
1020         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1021       }\r
1022   }\r
1023   SetCurrentDirectory(dir); /* return to prev directory */\r
1024 }\r
1025 \r
1026 VOID\r
1027 InitTextures()\r
1028 {\r
1029   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1030   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1031   \r
1032   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1033       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1034       liteBackTextureMode = appData.liteBackTextureMode;\r
1035 \r
1036       if (liteBackTexture == NULL && appData.debugMode) {\r
1037           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1038       }\r
1039   }\r
1040   \r
1041   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1042       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1043       darkBackTextureMode = appData.darkBackTextureMode;\r
1044 \r
1045       if (darkBackTexture == NULL && appData.debugMode) {\r
1046           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1047       }\r
1048   }\r
1049 }\r
1050 \r
1051 BOOL\r
1052 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1053 {\r
1054   HWND hwnd; /* Main window handle. */\r
1055   int ibs;\r
1056   WINDOWPLACEMENT wp;\r
1057   char *filepart;\r
1058 \r
1059   hInst = hInstance;    /* Store instance handle in our global variable */\r
1060   programName = szAppName;\r
1061 \r
1062   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1063     *filepart = NULLCHAR;\r
1064   } else {\r
1065     GetCurrentDirectory(MSG_SIZ, installDir);\r
1066   }\r
1067   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1068   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1069   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1070   /* xboard, and older WinBoards, controlled the move sound with the\r
1071      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1072      always turn the option on (so that the backend will call us),\r
1073      then let the user turn the sound off by setting it to silence if\r
1074      desired.  To accommodate old winboard.ini files saved by old\r
1075      versions of WinBoard, we also turn off the sound if the option\r
1076      was initially set to false. [HGM] taken out of InitAppData */\r
1077   if (!appData.ringBellAfterMoves) {\r
1078     sounds[(int)SoundMove].name = strdup("");\r
1079     appData.ringBellAfterMoves = TRUE;\r
1080   }\r
1081   if (appData.debugMode) {\r
1082     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1083     setbuf(debugFP, NULL);\r
1084   }\r
1085 \r
1086   LoadLanguageFile(appData.language);\r
1087 \r
1088   InitBackEnd1();\r
1089 \r
1090 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1091 //  InitEngineUCI( installDir, &second );\r
1092 \r
1093   /* Create a main window for this application instance. */\r
1094   hwnd = CreateWindow(szAppName, szTitle,\r
1095                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1096                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1097                       NULL, NULL, hInstance, NULL);\r
1098   hwndMain = hwnd;\r
1099 \r
1100   /* If window could not be created, return "failure" */\r
1101   if (!hwnd) {\r
1102     return (FALSE);\r
1103   }\r
1104 \r
1105   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1106   LoadLogo(&first, 0, FALSE);\r
1107   LoadLogo(&second, 1, appData.icsActive);\r
1108 \r
1109   SetUserLogo();\r
1110 \r
1111   iconWhite = LoadIcon(hInstance, "icon_white");\r
1112   iconBlack = LoadIcon(hInstance, "icon_black");\r
1113   iconCurrent = iconWhite;\r
1114   InitDrawingColors();\r
1115   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1116   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1117   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1118     /* Compute window size for each board size, and use the largest\r
1119        size that fits on this screen as the default. */\r
1120     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1121     if (boardSize == (BoardSize)-1 &&\r
1122         winH <= screenHeight\r
1123            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1124         && winW <= screenWidth) {\r
1125       boardSize = (BoardSize)ibs;\r
1126     }\r
1127   }\r
1128 \r
1129   InitDrawingSizes(boardSize, 0);\r
1130   RecentEngineMenu(appData.recentEngineList);\r
1131   InitMenuChecks();\r
1132   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1133 \r
1134   /* [AS] Load textures if specified */\r
1135   InitTextures();\r
1136 \r
1137   mysrandom( (unsigned) time(NULL) );\r
1138 \r
1139   /* [AS] Restore layout */\r
1140   if( wpMoveHistory.visible ) {\r
1141       MoveHistoryPopUp();\r
1142   }\r
1143 \r
1144   if( wpEvalGraph.visible ) {\r
1145       EvalGraphPopUp();\r
1146   }\r
1147 \r
1148   if( wpEngineOutput.visible ) {\r
1149       EngineOutputPopUp();\r
1150   }\r
1151 \r
1152   /* Make the window visible; update its client area; and return "success" */\r
1153   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1154   wp.length = sizeof(WINDOWPLACEMENT);\r
1155   wp.flags = 0;\r
1156   wp.showCmd = nCmdShow;\r
1157   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1158   wp.rcNormalPosition.left = wpMain.x;\r
1159   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1160   wp.rcNormalPosition.top = wpMain.y;\r
1161   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1162   SetWindowPlacement(hwndMain, &wp);\r
1163 \r
1164   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1165 \r
1166   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1167                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1168 \r
1169   if (hwndConsole) {\r
1170 #if AOT_CONSOLE\r
1171     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1172                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1173 #endif\r
1174     ShowWindow(hwndConsole, nCmdShow);\r
1175     SetActiveWindow(hwndConsole);\r
1176   }\r
1177   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1178   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1179 \r
1180   return TRUE;\r
1181 \r
1182 }\r
1183 \r
1184 VOID\r
1185 InitMenuChecks()\r
1186 {\r
1187   HMENU hmenu = GetMenu(hwndMain);\r
1188 \r
1189   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1190                         MF_BYCOMMAND|((appData.icsActive &&\r
1191                                        *appData.icsCommPort != NULLCHAR) ?\r
1192                                       MF_ENABLED : MF_GRAYED));\r
1193   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1194                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1195                                      MF_CHECKED : MF_UNCHECKED));\r
1196 }\r
1197 \r
1198 //---------------------------------------------------------------------------------------------------------\r
1199 \r
1200 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1201 #define XBOARD FALSE\r
1202 \r
1203 #define OPTCHAR "/"\r
1204 #define SEPCHAR "="\r
1205 #define TOPLEVEL 0\r
1206 \r
1207 #include "args.h"\r
1208 \r
1209 // front-end part of option handling\r
1210 \r
1211 VOID\r
1212 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1213 {\r
1214   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1215   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1216   DeleteDC(hdc);\r
1217   lf->lfWidth = 0;\r
1218   lf->lfEscapement = 0;\r
1219   lf->lfOrientation = 0;\r
1220   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1221   lf->lfItalic = mfp->italic;\r
1222   lf->lfUnderline = mfp->underline;\r
1223   lf->lfStrikeOut = mfp->strikeout;\r
1224   lf->lfCharSet = mfp->charset;\r
1225   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1226   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1227   lf->lfQuality = DEFAULT_QUALITY;\r
1228   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1229     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1230 }\r
1231 \r
1232 void\r
1233 CreateFontInMF(MyFont *mf)\r
1234\r
1235   LFfromMFP(&mf->lf, &mf->mfp);\r
1236   if (mf->hf) DeleteObject(mf->hf);\r
1237   mf->hf = CreateFontIndirect(&mf->lf);\r
1238 }\r
1239 \r
1240 // [HGM] This platform-dependent table provides the location for storing the color info\r
1241 void *\r
1242 colorVariable[] = {\r
1243   &whitePieceColor, \r
1244   &blackPieceColor, \r
1245   &lightSquareColor,\r
1246   &darkSquareColor, \r
1247   &highlightSquareColor,\r
1248   &premoveHighlightColor,\r
1249   NULL,\r
1250   &consoleBackgroundColor,\r
1251   &appData.fontForeColorWhite,\r
1252   &appData.fontBackColorWhite,\r
1253   &appData.fontForeColorBlack,\r
1254   &appData.fontBackColorBlack,\r
1255   &appData.evalHistColorWhite,\r
1256   &appData.evalHistColorBlack,\r
1257   &appData.highlightArrowColor,\r
1258 };\r
1259 \r
1260 /* Command line font name parser.  NULL name means do nothing.\r
1261    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1262    For backward compatibility, syntax without the colon is also\r
1263    accepted, but font names with digits in them won't work in that case.\r
1264 */\r
1265 VOID\r
1266 ParseFontName(char *name, MyFontParams *mfp)\r
1267 {\r
1268   char *p, *q;\r
1269   if (name == NULL) return;\r
1270   p = name;\r
1271   q = strchr(p, ':');\r
1272   if (q) {\r
1273     if (q - p >= sizeof(mfp->faceName))\r
1274       ExitArgError(_("Font name too long:"), name, TRUE);\r
1275     memcpy(mfp->faceName, p, q - p);\r
1276     mfp->faceName[q - p] = NULLCHAR;\r
1277     p = q + 1;\r
1278   } else {\r
1279     q = mfp->faceName;\r
1280     while (*p && !isdigit(*p)) {\r
1281       *q++ = *p++;\r
1282       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1283         ExitArgError(_("Font name too long:"), name, TRUE);\r
1284     }\r
1285     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1286     *q = NULLCHAR;\r
1287   }\r
1288   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1289   mfp->pointSize = (float) atof(p);\r
1290   mfp->bold = (strchr(p, 'b') != NULL);\r
1291   mfp->italic = (strchr(p, 'i') != NULL);\r
1292   mfp->underline = (strchr(p, 'u') != NULL);\r
1293   mfp->strikeout = (strchr(p, 's') != NULL);\r
1294   mfp->charset = DEFAULT_CHARSET;\r
1295   q = strchr(p, 'c');\r
1296   if (q)\r
1297     mfp->charset = (BYTE) atoi(q+1);\r
1298 }\r
1299 \r
1300 void\r
1301 ParseFont(char *name, int number)\r
1302 { // wrapper to shield back-end from 'font'\r
1303   ParseFontName(name, &font[boardSize][number]->mfp);\r
1304 }\r
1305 \r
1306 void\r
1307 SetFontDefaults()\r
1308 { // in WB  we have a 2D array of fonts; this initializes their description\r
1309   int i, j;\r
1310   /* Point font array elements to structures and\r
1311      parse default font names */\r
1312   for (i=0; i<NUM_FONTS; i++) {\r
1313     for (j=0; j<NUM_SIZES; j++) {\r
1314       font[j][i] = &fontRec[j][i];\r
1315       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1316     }\r
1317   }\r
1318 }\r
1319 \r
1320 void\r
1321 CreateFonts()\r
1322 { // here we create the actual fonts from the selected descriptions\r
1323   int i, j;\r
1324   for (i=0; i<NUM_FONTS; i++) {\r
1325     for (j=0; j<NUM_SIZES; j++) {\r
1326       CreateFontInMF(font[j][i]);\r
1327     }\r
1328   }\r
1329 }\r
1330 /* Color name parser.\r
1331    X version accepts X color names, but this one\r
1332    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1333 COLORREF\r
1334 ParseColorName(char *name)\r
1335 {\r
1336   int red, green, blue, count;\r
1337   char buf[MSG_SIZ];\r
1338 \r
1339   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1340   if (count != 3) {\r
1341     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1342       &red, &green, &blue);\r
1343   }\r
1344   if (count != 3) {\r
1345     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1346     DisplayError(buf, 0);\r
1347     return RGB(0, 0, 0);\r
1348   }\r
1349   return PALETTERGB(red, green, blue);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseColor(int n, char *name)\r
1354 { // for WinBoard the color is an int, which needs to be derived from the string\r
1355   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1356 }\r
1357 \r
1358 void\r
1359 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1360 {\r
1361   char *e = argValue;\r
1362   int eff = 0;\r
1363 \r
1364   while (*e) {\r
1365     if (*e == 'b')      eff |= CFE_BOLD;\r
1366     else if (*e == 'i') eff |= CFE_ITALIC;\r
1367     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1368     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1369     else if (*e == '#' || isdigit(*e)) break;\r
1370     e++;\r
1371   }\r
1372   *effects = eff;\r
1373   *color   = ParseColorName(e);\r
1374 }\r
1375 \r
1376 void\r
1377 ParseTextAttribs(ColorClass cc, char *s)\r
1378 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1379     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1380     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1381 }\r
1382 \r
1383 void\r
1384 ParseBoardSize(void *addr, char *name)\r
1385 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1386   BoardSize bs = SizeTiny;\r
1387   while (sizeInfo[bs].name != NULL) {\r
1388     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1389         *(BoardSize *)addr = bs;\r
1390         return;\r
1391     }\r
1392     bs++;\r
1393   }\r
1394   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1395 }\r
1396 \r
1397 void\r
1398 LoadAllSounds()\r
1399 { // [HGM] import name from appData first\r
1400   ColorClass cc;\r
1401   SoundClass sc;\r
1402   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1403     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1404     textAttribs[cc].sound.data = NULL;\r
1405     MyLoadSound(&textAttribs[cc].sound);\r
1406   }\r
1407   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1408     textAttribs[cc].sound.name = strdup("");\r
1409     textAttribs[cc].sound.data = NULL;\r
1410   }\r
1411   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1412     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1413     sounds[sc].data = NULL;\r
1414     MyLoadSound(&sounds[sc]);\r
1415   }\r
1416 }\r
1417 \r
1418 void\r
1419 SetCommPortDefaults()\r
1420 {\r
1421    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1422   dcb.DCBlength = sizeof(DCB);\r
1423   dcb.BaudRate = 9600;\r
1424   dcb.fBinary = TRUE;\r
1425   dcb.fParity = FALSE;\r
1426   dcb.fOutxCtsFlow = FALSE;\r
1427   dcb.fOutxDsrFlow = FALSE;\r
1428   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1429   dcb.fDsrSensitivity = FALSE;\r
1430   dcb.fTXContinueOnXoff = TRUE;\r
1431   dcb.fOutX = FALSE;\r
1432   dcb.fInX = FALSE;\r
1433   dcb.fNull = FALSE;\r
1434   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1435   dcb.fAbortOnError = FALSE;\r
1436   dcb.ByteSize = 7;\r
1437   dcb.Parity = SPACEPARITY;\r
1438   dcb.StopBits = ONESTOPBIT;\r
1439 }\r
1440 \r
1441 // [HGM] args: these three cases taken out to stay in front-end\r
1442 void\r
1443 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1444 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1445         // while the curent board size determines the element. This system should be ported to XBoard.\r
1446         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1447         int bs;\r
1448         for (bs=0; bs<NUM_SIZES; bs++) {\r
1449           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1450           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1451           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1452             ad->argName, mfp->faceName, mfp->pointSize,\r
1453             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1454             mfp->bold ? "b" : "",\r
1455             mfp->italic ? "i" : "",\r
1456             mfp->underline ? "u" : "",\r
1457             mfp->strikeout ? "s" : "",\r
1458             (int)mfp->charset);\r
1459         }\r
1460       }\r
1461 \r
1462 void\r
1463 ExportSounds()\r
1464 { // [HGM] copy the names from the internal WB variables to appData\r
1465   ColorClass cc;\r
1466   SoundClass sc;\r
1467   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1468     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1469   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1470     (&appData.soundMove)[sc] = sounds[sc].name;\r
1471 }\r
1472 \r
1473 void\r
1474 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1475 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1476         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1477         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1478           (ta->effects & CFE_BOLD) ? "b" : "",\r
1479           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1480           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1481           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1482           (ta->effects) ? " " : "",\r
1483           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1484       }\r
1485 \r
1486 void\r
1487 SaveColor(FILE *f, ArgDescriptor *ad)\r
1488 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1489         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1490         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1491           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1492 }\r
1493 \r
1494 void\r
1495 SaveBoardSize(FILE *f, char *name, void *addr)\r
1496 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1497   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1498 }\r
1499 \r
1500 void\r
1501 ParseCommPortSettings(char *s)\r
1502 { // wrapper to keep dcb from back-end\r
1503   ParseCommSettings(s, &dcb);\r
1504 }\r
1505 \r
1506 void\r
1507 GetWindowCoords()\r
1508 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1509   GetActualPlacement(hwndMain, &wpMain);\r
1510   GetActualPlacement(hwndConsole, &wpConsole);\r
1511   GetActualPlacement(commentDialog, &wpComment);\r
1512   GetActualPlacement(editTagsDialog, &wpTags);\r
1513   GetActualPlacement(gameListDialog, &wpGameList);\r
1514   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1515   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1516   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1517 }\r
1518 \r
1519 void\r
1520 PrintCommPortSettings(FILE *f, char *name)\r
1521 { // wrapper to shield back-end from DCB\r
1522       PrintCommSettings(f, name, &dcb);\r
1523 }\r
1524 \r
1525 int\r
1526 MySearchPath(char *installDir, char *name, char *fullname)\r
1527 {\r
1528   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1529   if(name[0]== '%') {\r
1530     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1531     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1532       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1533       *strchr(buf, '%') = 0;\r
1534       strcat(fullname, getenv(buf));\r
1535       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1536     }\r
1537     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1538     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1539     return (int) strlen(fullname);\r
1540   }\r
1541   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1542 }\r
1543 \r
1544 int\r
1545 MyGetFullPathName(char *name, char *fullname)\r
1546 {\r
1547   char *dummy;\r
1548   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1549 }\r
1550 \r
1551 int\r
1552 MainWindowUp()\r
1553 { // [HGM] args: allows testing if main window is realized from back-end\r
1554   return hwndMain != NULL;\r
1555 }\r
1556 \r
1557 void\r
1558 PopUpStartupDialog()\r
1559 {\r
1560     FARPROC lpProc;\r
1561     \r
1562     LoadLanguageFile(appData.language);\r
1563     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1564     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1565     FreeProcInstance(lpProc);\r
1566 }\r
1567 \r
1568 /*---------------------------------------------------------------------------*\\r
1569  *\r
1570  * GDI board drawing routines\r
1571  *\r
1572 \*---------------------------------------------------------------------------*/\r
1573 \r
1574 /* [AS] Draw square using background texture */\r
1575 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1576 {\r
1577     XFORM   x;\r
1578 \r
1579     if( mode == 0 ) {\r
1580         return; /* Should never happen! */\r
1581     }\r
1582 \r
1583     SetGraphicsMode( dst, GM_ADVANCED );\r
1584 \r
1585     switch( mode ) {\r
1586     case 1:\r
1587         /* Identity */\r
1588         break;\r
1589     case 2:\r
1590         /* X reflection */\r
1591         x.eM11 = -1.0;\r
1592         x.eM12 = 0;\r
1593         x.eM21 = 0;\r
1594         x.eM22 = 1.0;\r
1595         x.eDx = (FLOAT) dw + dx - 1;\r
1596         x.eDy = 0;\r
1597         dx = 0;\r
1598         SetWorldTransform( dst, &x );\r
1599         break;\r
1600     case 3:\r
1601         /* Y reflection */\r
1602         x.eM11 = 1.0;\r
1603         x.eM12 = 0;\r
1604         x.eM21 = 0;\r
1605         x.eM22 = -1.0;\r
1606         x.eDx = 0;\r
1607         x.eDy = (FLOAT) dh + dy - 1;\r
1608         dy = 0;\r
1609         SetWorldTransform( dst, &x );\r
1610         break;\r
1611     case 4:\r
1612         /* X/Y flip */\r
1613         x.eM11 = 0;\r
1614         x.eM12 = 1.0;\r
1615         x.eM21 = 1.0;\r
1616         x.eM22 = 0;\r
1617         x.eDx = (FLOAT) dx;\r
1618         x.eDy = (FLOAT) dy;\r
1619         dx = 0;\r
1620         dy = 0;\r
1621         SetWorldTransform( dst, &x );\r
1622         break;\r
1623     }\r
1624 \r
1625     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1626 \r
1627     x.eM11 = 1.0;\r
1628     x.eM12 = 0;\r
1629     x.eM21 = 0;\r
1630     x.eM22 = 1.0;\r
1631     x.eDx = 0;\r
1632     x.eDy = 0;\r
1633     SetWorldTransform( dst, &x );\r
1634 \r
1635     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1636 }\r
1637 \r
1638 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1639 enum {\r
1640     PM_WP = (int) WhitePawn, \r
1641     PM_WN = (int) WhiteKnight, \r
1642     PM_WB = (int) WhiteBishop, \r
1643     PM_WR = (int) WhiteRook, \r
1644     PM_WQ = (int) WhiteQueen, \r
1645     PM_WF = (int) WhiteFerz, \r
1646     PM_WW = (int) WhiteWazir, \r
1647     PM_WE = (int) WhiteAlfil, \r
1648     PM_WM = (int) WhiteMan, \r
1649     PM_WO = (int) WhiteCannon, \r
1650     PM_WU = (int) WhiteUnicorn, \r
1651     PM_WH = (int) WhiteNightrider, \r
1652     PM_WA = (int) WhiteAngel, \r
1653     PM_WC = (int) WhiteMarshall, \r
1654     PM_WAB = (int) WhiteCardinal, \r
1655     PM_WD = (int) WhiteDragon, \r
1656     PM_WL = (int) WhiteLance, \r
1657     PM_WS = (int) WhiteCobra, \r
1658     PM_WV = (int) WhiteFalcon, \r
1659     PM_WSG = (int) WhiteSilver, \r
1660     PM_WG = (int) WhiteGrasshopper, \r
1661     PM_WK = (int) WhiteKing,\r
1662     PM_BP = (int) BlackPawn, \r
1663     PM_BN = (int) BlackKnight, \r
1664     PM_BB = (int) BlackBishop, \r
1665     PM_BR = (int) BlackRook, \r
1666     PM_BQ = (int) BlackQueen, \r
1667     PM_BF = (int) BlackFerz, \r
1668     PM_BW = (int) BlackWazir, \r
1669     PM_BE = (int) BlackAlfil, \r
1670     PM_BM = (int) BlackMan,\r
1671     PM_BO = (int) BlackCannon, \r
1672     PM_BU = (int) BlackUnicorn, \r
1673     PM_BH = (int) BlackNightrider, \r
1674     PM_BA = (int) BlackAngel, \r
1675     PM_BC = (int) BlackMarshall, \r
1676     PM_BG = (int) BlackGrasshopper, \r
1677     PM_BAB = (int) BlackCardinal,\r
1678     PM_BD = (int) BlackDragon,\r
1679     PM_BL = (int) BlackLance,\r
1680     PM_BS = (int) BlackCobra,\r
1681     PM_BV = (int) BlackFalcon,\r
1682     PM_BSG = (int) BlackSilver,\r
1683     PM_BK = (int) BlackKing\r
1684 };\r
1685 \r
1686 static HFONT hPieceFont = NULL;\r
1687 static HBITMAP hPieceMask[(int) EmptySquare];\r
1688 static HBITMAP hPieceFace[(int) EmptySquare];\r
1689 static int fontBitmapSquareSize = 0;\r
1690 static char pieceToFontChar[(int) EmptySquare] =\r
1691                               { 'p', 'n', 'b', 'r', 'q', \r
1692                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1693                       'k', 'o', 'm', 'v', 't', 'w', \r
1694                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1695                                                               'l' };\r
1696 \r
1697 extern BOOL SetCharTable( char *table, const char * map );\r
1698 /* [HGM] moved to backend.c */\r
1699 \r
1700 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1701 {\r
1702     HBRUSH hbrush;\r
1703     BYTE r1 = GetRValue( color );\r
1704     BYTE g1 = GetGValue( color );\r
1705     BYTE b1 = GetBValue( color );\r
1706     BYTE r2 = r1 / 2;\r
1707     BYTE g2 = g1 / 2;\r
1708     BYTE b2 = b1 / 2;\r
1709     RECT rc;\r
1710 \r
1711     /* Create a uniform background first */\r
1712     hbrush = CreateSolidBrush( color );\r
1713     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1714     FillRect( hdc, &rc, hbrush );\r
1715     DeleteObject( hbrush );\r
1716     \r
1717     if( mode == 1 ) {\r
1718         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1719         int steps = squareSize / 2;\r
1720         int i;\r
1721 \r
1722         for( i=0; i<steps; i++ ) {\r
1723             BYTE r = r1 - (r1-r2) * i / steps;\r
1724             BYTE g = g1 - (g1-g2) * i / steps;\r
1725             BYTE b = b1 - (b1-b2) * i / steps;\r
1726 \r
1727             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1728             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1729             FillRect( hdc, &rc, hbrush );\r
1730             DeleteObject(hbrush);\r
1731         }\r
1732     }\r
1733     else if( mode == 2 ) {\r
1734         /* Diagonal gradient, good more or less for every piece */\r
1735         POINT triangle[3];\r
1736         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1737         HBRUSH hbrush_old;\r
1738         int steps = squareSize;\r
1739         int i;\r
1740 \r
1741         triangle[0].x = squareSize - steps;\r
1742         triangle[0].y = squareSize;\r
1743         triangle[1].x = squareSize;\r
1744         triangle[1].y = squareSize;\r
1745         triangle[2].x = squareSize;\r
1746         triangle[2].y = squareSize - steps;\r
1747 \r
1748         for( i=0; i<steps; i++ ) {\r
1749             BYTE r = r1 - (r1-r2) * i / steps;\r
1750             BYTE g = g1 - (g1-g2) * i / steps;\r
1751             BYTE b = b1 - (b1-b2) * i / steps;\r
1752 \r
1753             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1754             hbrush_old = SelectObject( hdc, hbrush );\r
1755             Polygon( hdc, triangle, 3 );\r
1756             SelectObject( hdc, hbrush_old );\r
1757             DeleteObject(hbrush);\r
1758             triangle[0].x++;\r
1759             triangle[2].y++;\r
1760         }\r
1761 \r
1762         SelectObject( hdc, hpen );\r
1763     }\r
1764 }\r
1765 \r
1766 /*\r
1767     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1768     seems to work ok. The main problem here is to find the "inside" of a chess\r
1769     piece: follow the steps as explained below.\r
1770 */\r
1771 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1772 {\r
1773     HBITMAP hbm;\r
1774     HBITMAP hbm_old;\r
1775     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1776     RECT rc;\r
1777     SIZE sz;\r
1778     POINT pt;\r
1779     int backColor = whitePieceColor; \r
1780     int foreColor = blackPieceColor;\r
1781     \r
1782     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1783         backColor = appData.fontBackColorWhite;\r
1784         foreColor = appData.fontForeColorWhite;\r
1785     }\r
1786     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1787         backColor = appData.fontBackColorBlack;\r
1788         foreColor = appData.fontForeColorBlack;\r
1789     }\r
1790 \r
1791     /* Mask */\r
1792     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1793 \r
1794     hbm_old = SelectObject( hdc, hbm );\r
1795 \r
1796     rc.left = 0;\r
1797     rc.top = 0;\r
1798     rc.right = squareSize;\r
1799     rc.bottom = squareSize;\r
1800 \r
1801     /* Step 1: background is now black */\r
1802     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1803 \r
1804     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1805 \r
1806     pt.x = (squareSize - sz.cx) / 2;\r
1807     pt.y = (squareSize - sz.cy) / 2;\r
1808 \r
1809     SetBkMode( hdc, TRANSPARENT );\r
1810     SetTextColor( hdc, chroma );\r
1811     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1812     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1813 \r
1814     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1815     /* Step 3: the area outside the piece is filled with white */\r
1816 //    FloodFill( hdc, 0, 0, chroma );\r
1817     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1818     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1819     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1820     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1821     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1822     /* \r
1823         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1824         but if the start point is not inside the piece we're lost!\r
1825         There should be a better way to do this... if we could create a region or path\r
1826         from the fill operation we would be fine for example.\r
1827     */\r
1828 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1829     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1830 \r
1831     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1832         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1833         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1834 \r
1835         SelectObject( dc2, bm2 );\r
1836         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1837         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1838         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1839         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1840         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1841 \r
1842         DeleteDC( dc2 );\r
1843         DeleteObject( bm2 );\r
1844     }\r
1845 \r
1846     SetTextColor( hdc, 0 );\r
1847     /* \r
1848         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1849         draw the piece again in black for safety.\r
1850     */\r
1851     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1852 \r
1853     SelectObject( hdc, hbm_old );\r
1854 \r
1855     if( hPieceMask[index] != NULL ) {\r
1856         DeleteObject( hPieceMask[index] );\r
1857     }\r
1858 \r
1859     hPieceMask[index] = hbm;\r
1860 \r
1861     /* Face */\r
1862     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1863 \r
1864     SelectObject( hdc, hbm );\r
1865 \r
1866     {\r
1867         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1868         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1869         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1870 \r
1871         SelectObject( dc1, hPieceMask[index] );\r
1872         SelectObject( dc2, bm2 );\r
1873         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1874         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1875         \r
1876         /* \r
1877             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1878             the piece background and deletes (makes transparent) the rest.\r
1879             Thanks to that mask, we are free to paint the background with the greates\r
1880             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1881             We use this, to make gradients and give the pieces a "roundish" look.\r
1882         */\r
1883         SetPieceBackground( hdc, backColor, 2 );\r
1884         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1885 \r
1886         DeleteDC( dc2 );\r
1887         DeleteDC( dc1 );\r
1888         DeleteObject( bm2 );\r
1889     }\r
1890 \r
1891     SetTextColor( hdc, foreColor );\r
1892     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1893 \r
1894     SelectObject( hdc, hbm_old );\r
1895 \r
1896     if( hPieceFace[index] != NULL ) {\r
1897         DeleteObject( hPieceFace[index] );\r
1898     }\r
1899 \r
1900     hPieceFace[index] = hbm;\r
1901 }\r
1902 \r
1903 static int TranslatePieceToFontPiece( int piece )\r
1904 {\r
1905     switch( piece ) {\r
1906     case BlackPawn:\r
1907         return PM_BP;\r
1908     case BlackKnight:\r
1909         return PM_BN;\r
1910     case BlackBishop:\r
1911         return PM_BB;\r
1912     case BlackRook:\r
1913         return PM_BR;\r
1914     case BlackQueen:\r
1915         return PM_BQ;\r
1916     case BlackKing:\r
1917         return PM_BK;\r
1918     case WhitePawn:\r
1919         return PM_WP;\r
1920     case WhiteKnight:\r
1921         return PM_WN;\r
1922     case WhiteBishop:\r
1923         return PM_WB;\r
1924     case WhiteRook:\r
1925         return PM_WR;\r
1926     case WhiteQueen:\r
1927         return PM_WQ;\r
1928     case WhiteKing:\r
1929         return PM_WK;\r
1930 \r
1931     case BlackAngel:\r
1932         return PM_BA;\r
1933     case BlackMarshall:\r
1934         return PM_BC;\r
1935     case BlackFerz:\r
1936         return PM_BF;\r
1937     case BlackNightrider:\r
1938         return PM_BH;\r
1939     case BlackAlfil:\r
1940         return PM_BE;\r
1941     case BlackWazir:\r
1942         return PM_BW;\r
1943     case BlackUnicorn:\r
1944         return PM_BU;\r
1945     case BlackCannon:\r
1946         return PM_BO;\r
1947     case BlackGrasshopper:\r
1948         return PM_BG;\r
1949     case BlackMan:\r
1950         return PM_BM;\r
1951     case BlackSilver:\r
1952         return PM_BSG;\r
1953     case BlackLance:\r
1954         return PM_BL;\r
1955     case BlackFalcon:\r
1956         return PM_BV;\r
1957     case BlackCobra:\r
1958         return PM_BS;\r
1959     case BlackCardinal:\r
1960         return PM_BAB;\r
1961     case BlackDragon:\r
1962         return PM_BD;\r
1963 \r
1964     case WhiteAngel:\r
1965         return PM_WA;\r
1966     case WhiteMarshall:\r
1967         return PM_WC;\r
1968     case WhiteFerz:\r
1969         return PM_WF;\r
1970     case WhiteNightrider:\r
1971         return PM_WH;\r
1972     case WhiteAlfil:\r
1973         return PM_WE;\r
1974     case WhiteWazir:\r
1975         return PM_WW;\r
1976     case WhiteUnicorn:\r
1977         return PM_WU;\r
1978     case WhiteCannon:\r
1979         return PM_WO;\r
1980     case WhiteGrasshopper:\r
1981         return PM_WG;\r
1982     case WhiteMan:\r
1983         return PM_WM;\r
1984     case WhiteSilver:\r
1985         return PM_WSG;\r
1986     case WhiteLance:\r
1987         return PM_WL;\r
1988     case WhiteFalcon:\r
1989         return PM_WV;\r
1990     case WhiteCobra:\r
1991         return PM_WS;\r
1992     case WhiteCardinal:\r
1993         return PM_WAB;\r
1994     case WhiteDragon:\r
1995         return PM_WD;\r
1996     }\r
1997 \r
1998     return 0;\r
1999 }\r
2000 \r
2001 void CreatePiecesFromFont()\r
2002 {\r
2003     LOGFONT lf;\r
2004     HDC hdc_window = NULL;\r
2005     HDC hdc = NULL;\r
2006     HFONT hfont_old;\r
2007     int fontHeight;\r
2008     int i;\r
2009 \r
2010     if( fontBitmapSquareSize < 0 ) {\r
2011         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2012         return;\r
2013     }\r
2014 \r
2015     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2016             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2017         fontBitmapSquareSize = -1;\r
2018         return;\r
2019     }\r
2020 \r
2021     if( fontBitmapSquareSize != squareSize ) {\r
2022         hdc_window = GetDC( hwndMain );\r
2023         hdc = CreateCompatibleDC( hdc_window );\r
2024 \r
2025         if( hPieceFont != NULL ) {\r
2026             DeleteObject( hPieceFont );\r
2027         }\r
2028         else {\r
2029             for( i=0; i<=(int)BlackKing; i++ ) {\r
2030                 hPieceMask[i] = NULL;\r
2031                 hPieceFace[i] = NULL;\r
2032             }\r
2033         }\r
2034 \r
2035         fontHeight = 75;\r
2036 \r
2037         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2038             fontHeight = appData.fontPieceSize;\r
2039         }\r
2040 \r
2041         fontHeight = (fontHeight * squareSize) / 100;\r
2042 \r
2043         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2044         lf.lfWidth = 0;\r
2045         lf.lfEscapement = 0;\r
2046         lf.lfOrientation = 0;\r
2047         lf.lfWeight = FW_NORMAL;\r
2048         lf.lfItalic = 0;\r
2049         lf.lfUnderline = 0;\r
2050         lf.lfStrikeOut = 0;\r
2051         lf.lfCharSet = DEFAULT_CHARSET;\r
2052         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2053         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2054         lf.lfQuality = PROOF_QUALITY;\r
2055         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2056         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2057         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2058 \r
2059         hPieceFont = CreateFontIndirect( &lf );\r
2060 \r
2061         if( hPieceFont == NULL ) {\r
2062             fontBitmapSquareSize = -2;\r
2063         }\r
2064         else {\r
2065             /* Setup font-to-piece character table */\r
2066             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2067                 /* No (or wrong) global settings, try to detect the font */\r
2068                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2069                     /* Alpha */\r
2070                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2071                 }\r
2072                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2073                     /* DiagramTT* family */\r
2074                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2075                 }\r
2076                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2077                     /* Fairy symbols */\r
2078                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2079                 }\r
2080                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2081                     /* Good Companion (Some characters get warped as literal :-( */\r
2082                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2083                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2084                     SetCharTable(pieceToFontChar, s);\r
2085                 }\r
2086                 else {\r
2087                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2088                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2089                 }\r
2090             }\r
2091 \r
2092             /* Create bitmaps */\r
2093             hfont_old = SelectObject( hdc, hPieceFont );\r
2094             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2095                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2096                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2097 \r
2098             SelectObject( hdc, hfont_old );\r
2099 \r
2100             fontBitmapSquareSize = squareSize;\r
2101         }\r
2102     }\r
2103 \r
2104     if( hdc != NULL ) {\r
2105         DeleteDC( hdc );\r
2106     }\r
2107 \r
2108     if( hdc_window != NULL ) {\r
2109         ReleaseDC( hwndMain, hdc_window );\r
2110     }\r
2111 }\r
2112 \r
2113 HBITMAP\r
2114 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2115 {\r
2116   char name[128];\r
2117 \r
2118     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2119   if (gameInfo.event &&\r
2120       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2121       strcmp(name, "k80s") == 0) {\r
2122     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2123   }\r
2124   return LoadBitmap(hinst, name);\r
2125 }\r
2126 \r
2127 \r
2128 /* Insert a color into the program's logical palette\r
2129    structure.  This code assumes the given color is\r
2130    the result of the RGB or PALETTERGB macro, and it\r
2131    knows how those macros work (which is documented).\r
2132 */\r
2133 VOID\r
2134 InsertInPalette(COLORREF color)\r
2135 {\r
2136   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2137 \r
2138   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2139     DisplayFatalError(_("Too many colors"), 0, 1);\r
2140     pLogPal->palNumEntries--;\r
2141     return;\r
2142   }\r
2143 \r
2144   pe->peFlags = (char) 0;\r
2145   pe->peRed = (char) (0xFF & color);\r
2146   pe->peGreen = (char) (0xFF & (color >> 8));\r
2147   pe->peBlue = (char) (0xFF & (color >> 16));\r
2148   return;\r
2149 }\r
2150 \r
2151 \r
2152 VOID\r
2153 InitDrawingColors()\r
2154 {\r
2155   if (pLogPal == NULL) {\r
2156     /* Allocate enough memory for a logical palette with\r
2157      * PALETTESIZE entries and set the size and version fields\r
2158      * of the logical palette structure.\r
2159      */\r
2160     pLogPal = (NPLOGPALETTE)\r
2161       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2162                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2163     pLogPal->palVersion    = 0x300;\r
2164   }\r
2165   pLogPal->palNumEntries = 0;\r
2166 \r
2167   InsertInPalette(lightSquareColor);\r
2168   InsertInPalette(darkSquareColor);\r
2169   InsertInPalette(whitePieceColor);\r
2170   InsertInPalette(blackPieceColor);\r
2171   InsertInPalette(highlightSquareColor);\r
2172   InsertInPalette(premoveHighlightColor);\r
2173 \r
2174   /*  create a logical color palette according the information\r
2175    *  in the LOGPALETTE structure.\r
2176    */\r
2177   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2178 \r
2179   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2180   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2181   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2182   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2183   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2184   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2185   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2186   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2187   /* [AS] Force rendering of the font-based pieces */\r
2188   if( fontBitmapSquareSize > 0 ) {\r
2189     fontBitmapSquareSize = 0;\r
2190   }\r
2191 }\r
2192 \r
2193 \r
2194 int\r
2195 BoardWidth(int boardSize, int n)\r
2196 { /* [HGM] argument n added to allow different width and height */\r
2197   int lineGap = sizeInfo[boardSize].lineGap;\r
2198 \r
2199   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2200       lineGap = appData.overrideLineGap;\r
2201   }\r
2202 \r
2203   return (n + 1) * lineGap +\r
2204           n * sizeInfo[boardSize].squareSize;\r
2205 }\r
2206 \r
2207 /* Respond to board resize by dragging edge */\r
2208 VOID\r
2209 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2210 {\r
2211   BoardSize newSize = NUM_SIZES - 1;\r
2212   static int recurse = 0;\r
2213   if (IsIconic(hwndMain)) return;\r
2214   if (recurse > 0) return;\r
2215   recurse++;\r
2216   while (newSize > 0) {\r
2217         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2218         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2219            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2220     newSize--;\r
2221   } \r
2222   boardSize = newSize;\r
2223   InitDrawingSizes(boardSize, flags);\r
2224   recurse--;\r
2225 }\r
2226 \r
2227 \r
2228 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2229 \r
2230 VOID\r
2231 InitDrawingSizes(BoardSize boardSize, int flags)\r
2232 {\r
2233   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2234   ChessSquare piece;\r
2235   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2236   HDC hdc;\r
2237   SIZE clockSize, messageSize;\r
2238   HFONT oldFont;\r
2239   char buf[MSG_SIZ];\r
2240   char *str;\r
2241   HMENU hmenu = GetMenu(hwndMain);\r
2242   RECT crect, wrect, oldRect;\r
2243   int offby;\r
2244   LOGBRUSH logbrush;\r
2245   VariantClass v = gameInfo.variant;\r
2246 \r
2247   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2248   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2249 \r
2250   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2251   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2252   oldBoardSize = boardSize;\r
2253 \r
2254   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2255   { // correct board size to one where built-in pieces exist\r
2256     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2257        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2258       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2259       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2260       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2261       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2262       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2263                                    boardSize = SizeMiddling;\r
2264     }\r
2265   }\r
2266   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2267 \r
2268   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2269   oldRect.top = wpMain.y;\r
2270   oldRect.right = wpMain.x + wpMain.width;\r
2271   oldRect.bottom = wpMain.y + wpMain.height;\r
2272 \r
2273   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2274   smallLayout = sizeInfo[boardSize].smallLayout;\r
2275   squareSize = sizeInfo[boardSize].squareSize;\r
2276   lineGap = sizeInfo[boardSize].lineGap;\r
2277   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2278 \r
2279   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2280       lineGap = appData.overrideLineGap;\r
2281   }\r
2282 \r
2283   if (tinyLayout != oldTinyLayout) {\r
2284     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2285     if (tinyLayout) {\r
2286       style &= ~WS_SYSMENU;\r
2287       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2288                  "&Minimize\tCtrl+F4");\r
2289     } else {\r
2290       style |= WS_SYSMENU;\r
2291       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2292     }\r
2293     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2294 \r
2295     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2296       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2297         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2298     }\r
2299     DrawMenuBar(hwndMain);\r
2300   }\r
2301 \r
2302   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2303   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2304 \r
2305   /* Get text area sizes */\r
2306   hdc = GetDC(hwndMain);\r
2307   if (appData.clockMode) {\r
2308     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2309   } else {\r
2310     snprintf(buf, MSG_SIZ, _("White"));\r
2311   }\r
2312   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2313   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2314   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2315   str = _("We only care about the height here");\r
2316   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2317   SelectObject(hdc, oldFont);\r
2318   ReleaseDC(hwndMain, hdc);\r
2319 \r
2320   /* Compute where everything goes */\r
2321   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2322         /* [HGM] logo: if either logo is on, reserve space for it */\r
2323         logoHeight =  2*clockSize.cy;\r
2324         leftLogoRect.left   = OUTER_MARGIN;\r
2325         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2326         leftLogoRect.top    = OUTER_MARGIN;\r
2327         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2328 \r
2329         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2330         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2331         rightLogoRect.top    = OUTER_MARGIN;\r
2332         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2333 \r
2334 \r
2335     whiteRect.left = leftLogoRect.right;\r
2336     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2337     whiteRect.top = OUTER_MARGIN;\r
2338     whiteRect.bottom = whiteRect.top + logoHeight;\r
2339 \r
2340     blackRect.right = rightLogoRect.left;\r
2341     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2342     blackRect.top = whiteRect.top;\r
2343     blackRect.bottom = whiteRect.bottom;\r
2344   } else {\r
2345     whiteRect.left = OUTER_MARGIN;\r
2346     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2347     whiteRect.top = OUTER_MARGIN;\r
2348     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2349 \r
2350     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2351     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2352     blackRect.top = whiteRect.top;\r
2353     blackRect.bottom = whiteRect.bottom;\r
2354 \r
2355     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2356   }\r
2357 \r
2358   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2359   if (appData.showButtonBar) {\r
2360     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2361       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2362   } else {\r
2363     messageRect.right = OUTER_MARGIN + boardWidth;\r
2364   }\r
2365   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2366   messageRect.bottom = messageRect.top + messageSize.cy;\r
2367 \r
2368   boardRect.left = OUTER_MARGIN;\r
2369   boardRect.right = boardRect.left + boardWidth;\r
2370   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2371   boardRect.bottom = boardRect.top + boardHeight;\r
2372 \r
2373   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2374   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2375   oldTinyLayout = tinyLayout;\r
2376   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2377   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2378     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2379   winW *= 1 + twoBoards;\r
2380   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2381   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2382   wpMain.height = winH; //       without disturbing window attachments\r
2383   GetWindowRect(hwndMain, &wrect);\r
2384   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2385                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2386 \r
2387   // [HGM] placement: let attached windows follow size change.\r
2388   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2389   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2390   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2391   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2392   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2393 \r
2394   /* compensate if menu bar wrapped */\r
2395   GetClientRect(hwndMain, &crect);\r
2396   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2397   wpMain.height += offby;\r
2398   switch (flags) {\r
2399   case WMSZ_TOPLEFT:\r
2400     SetWindowPos(hwndMain, NULL, \r
2401                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2402                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2403     break;\r
2404 \r
2405   case WMSZ_TOPRIGHT:\r
2406   case WMSZ_TOP:\r
2407     SetWindowPos(hwndMain, NULL, \r
2408                  wrect.left, wrect.bottom - wpMain.height, \r
2409                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2410     break;\r
2411 \r
2412   case WMSZ_BOTTOMLEFT:\r
2413   case WMSZ_LEFT:\r
2414     SetWindowPos(hwndMain, NULL, \r
2415                  wrect.right - wpMain.width, wrect.top, \r
2416                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2417     break;\r
2418 \r
2419   case WMSZ_BOTTOMRIGHT:\r
2420   case WMSZ_BOTTOM:\r
2421   case WMSZ_RIGHT:\r
2422   default:\r
2423     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2424                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2425     break;\r
2426   }\r
2427 \r
2428   hwndPause = NULL;\r
2429   for (i = 0; i < N_BUTTONS; i++) {\r
2430     if (buttonDesc[i].hwnd != NULL) {\r
2431       DestroyWindow(buttonDesc[i].hwnd);\r
2432       buttonDesc[i].hwnd = NULL;\r
2433     }\r
2434     if (appData.showButtonBar) {\r
2435       buttonDesc[i].hwnd =\r
2436         CreateWindow("BUTTON", buttonDesc[i].label,\r
2437                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2438                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2439                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2440                      (HMENU) buttonDesc[i].id,\r
2441                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2442       if (tinyLayout) {\r
2443         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2444                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2445                     MAKELPARAM(FALSE, 0));\r
2446       }\r
2447       if (buttonDesc[i].id == IDM_Pause)\r
2448         hwndPause = buttonDesc[i].hwnd;\r
2449       buttonDesc[i].wndproc = (WNDPROC)\r
2450         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2451     }\r
2452   }\r
2453   if (gridPen != NULL) DeleteObject(gridPen);\r
2454   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2455   if (premovePen != NULL) DeleteObject(premovePen);\r
2456   if (lineGap != 0) {\r
2457     logbrush.lbStyle = BS_SOLID;\r
2458     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2459     gridPen =\r
2460       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2461                    lineGap, &logbrush, 0, NULL);\r
2462     logbrush.lbColor = highlightSquareColor;\r
2463     highlightPen =\r
2464       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2465                    lineGap, &logbrush, 0, NULL);\r
2466 \r
2467     logbrush.lbColor = premoveHighlightColor; \r
2468     premovePen =\r
2469       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2470                    lineGap, &logbrush, 0, NULL);\r
2471 \r
2472     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2473     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2474       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2475       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2476         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2477       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2478         BOARD_WIDTH * (squareSize + lineGap);\r
2479       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2480     }\r
2481     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2482       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2483       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2484         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2485         lineGap / 2 + (i * (squareSize + lineGap));\r
2486       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2487         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2488       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2489     }\r
2490   }\r
2491 \r
2492   /* [HGM] Licensing requirement */\r
2493 #ifdef GOTHIC\r
2494   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2495 #endif\r
2496 #ifdef FALCON\r
2497   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2498 #endif\r
2499   GothicPopUp( "", VariantNormal);\r
2500 \r
2501 \r
2502 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2503 \r
2504   /* Load piece bitmaps for this board size */\r
2505   for (i=0; i<=2; i++) {\r
2506     for (piece = WhitePawn;\r
2507          (int) piece < (int) BlackPawn;\r
2508          piece = (ChessSquare) ((int) piece + 1)) {\r
2509       if (pieceBitmap[i][piece] != NULL)\r
2510         DeleteObject(pieceBitmap[i][piece]);\r
2511     }\r
2512   }\r
2513 \r
2514   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2515   // Orthodox Chess pieces\r
2516   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2517   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2518   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2519   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2520   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2521   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2522   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2523   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2524   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2525   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2526   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2527   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2528   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2529   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2530   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2531   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2532     // in Shogi, Hijack the unused Queen for Lance\r
2533     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2534     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2535     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2536   } else {\r
2537     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2538     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2539     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2540   }\r
2541 \r
2542   if(squareSize <= 72 && squareSize >= 33) { \r
2543     /* A & C are available in most sizes now */\r
2544     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2545       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2546       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2547       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2548       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2549       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2550       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2551       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2552       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2553       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2554       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2555       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2556       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2557     } else { // Smirf-like\r
2558       if(gameInfo.variant == VariantSChess) {\r
2559         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2560         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2561         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2562       } else {\r
2563         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2564         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2565         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2566       }\r
2567     }\r
2568     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2569       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2570       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2571       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2572     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2573       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2574       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2575       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2576     } else { // WinBoard standard\r
2577       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2578       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2579       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2580     }\r
2581   }\r
2582 \r
2583 \r
2584   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2585     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2586     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2587     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2588     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2589     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2590     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2591     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2592     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2593     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2594     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2595     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2596     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2597     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2598     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2599     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2600     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2601     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2602     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2603     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2604     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2605     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2606     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2607     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2608     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2609     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2610     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2611     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2612     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2613     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2614     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2615 \r
2616     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2617       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2618       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2619       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2620       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2621       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2622       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2623       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2624       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2625       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2626       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2627       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2628       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2629     } else {\r
2630       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2631       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2632       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2633       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2634       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2635       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2636       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2637       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2638       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2639       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2640       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2641       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2642     }\r
2643 \r
2644   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2645     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2646     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2647     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2648     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2649     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2650     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2651     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2652     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2653     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2654     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2655     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2656     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2657     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2658     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2659   }\r
2660 \r
2661 \r
2662   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2663   /* special Shogi support in this size */\r
2664   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2665       for (piece = WhitePawn;\r
2666            (int) piece < (int) BlackPawn;\r
2667            piece = (ChessSquare) ((int) piece + 1)) {\r
2668         if (pieceBitmap[i][piece] != NULL)\r
2669           DeleteObject(pieceBitmap[i][piece]);\r
2670       }\r
2671     }\r
2672   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2673   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2674   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2675   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2676   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2677   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2678   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2679   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2680   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2681   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2682   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2683   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2684   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2685   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2686   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2687   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2688   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2689   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2690   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2691   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2692   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2693   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2694   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2695   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2696   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2697   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2698   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2699   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2700   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2701   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2702   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2703   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2704   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2705   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2706   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2707   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2708   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2709   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2710   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2711   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2712   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2713   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2714   minorSize = 0;\r
2715   }\r
2716 }\r
2717 \r
2718 HBITMAP\r
2719 PieceBitmap(ChessSquare p, int kind)\r
2720 {\r
2721   if ((int) p >= (int) BlackPawn)\r
2722     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2723 \r
2724   return pieceBitmap[kind][(int) p];\r
2725 }\r
2726 \r
2727 /***************************************************************/\r
2728 \r
2729 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2730 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2731 /*\r
2732 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2733 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2734 */\r
2735 \r
2736 VOID\r
2737 SquareToPos(int row, int column, int * x, int * y)\r
2738 {\r
2739   if (flipView) {\r
2740     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2741     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2742   } else {\r
2743     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2744     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2745   }\r
2746 }\r
2747 \r
2748 VOID\r
2749 DrawCoordsOnDC(HDC hdc)\r
2750 {\r
2751   static char files[] = "0123456789012345678901221098765432109876543210";\r
2752   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2753   char str[2] = { NULLCHAR, NULLCHAR };\r
2754   int oldMode, oldAlign, x, y, start, i;\r
2755   HFONT oldFont;\r
2756   HBRUSH oldBrush;\r
2757 \r
2758   if (!appData.showCoords)\r
2759     return;\r
2760 \r
2761   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2762 \r
2763   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2764   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2765   oldAlign = GetTextAlign(hdc);\r
2766   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2767 \r
2768   y = boardRect.top + lineGap;\r
2769   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2770 \r
2771   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2772   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2773     str[0] = files[start + i];\r
2774     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2775     y += squareSize + lineGap;\r
2776   }\r
2777 \r
2778   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2779 \r
2780   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2781   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2782     str[0] = ranks[start + i];\r
2783     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2784     x += squareSize + lineGap;\r
2785   }    \r
2786 \r
2787   SelectObject(hdc, oldBrush);\r
2788   SetBkMode(hdc, oldMode);\r
2789   SetTextAlign(hdc, oldAlign);\r
2790   SelectObject(hdc, oldFont);\r
2791 }\r
2792 \r
2793 VOID\r
2794 DrawGridOnDC(HDC hdc)\r
2795 {\r
2796   HPEN oldPen;\r
2797  \r
2798   if (lineGap != 0) {\r
2799     oldPen = SelectObject(hdc, gridPen);\r
2800     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2801     SelectObject(hdc, oldPen);\r
2802   }\r
2803 }\r
2804 \r
2805 #define HIGHLIGHT_PEN 0\r
2806 #define PREMOVE_PEN   1\r
2807 \r
2808 VOID\r
2809 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2810 {\r
2811   int x1, y1;\r
2812   HPEN oldPen, hPen;\r
2813   if (lineGap == 0) return;\r
2814   if (flipView) {\r
2815     x1 = boardRect.left +\r
2816       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2817     y1 = boardRect.top +\r
2818       lineGap/2 + y * (squareSize + lineGap);\r
2819   } else {\r
2820     x1 = boardRect.left +\r
2821       lineGap/2 + x * (squareSize + lineGap);\r
2822     y1 = boardRect.top +\r
2823       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2824   }\r
2825   hPen = pen ? premovePen : highlightPen;\r
2826   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2827   MoveToEx(hdc, x1, y1, NULL);\r
2828   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2829   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2830   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2831   LineTo(hdc, x1, y1);\r
2832   SelectObject(hdc, oldPen);\r
2833 }\r
2834 \r
2835 VOID\r
2836 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2837 {\r
2838   int i;\r
2839   for (i=0; i<2; i++) {\r
2840     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2841       DrawHighlightOnDC(hdc, TRUE,\r
2842                         h->sq[i].x, h->sq[i].y,\r
2843                         pen);\r
2844   }\r
2845 }\r
2846 \r
2847 /* Note: sqcolor is used only in monoMode */\r
2848 /* Note that this code is largely duplicated in woptions.c,\r
2849    function DrawSampleSquare, so that needs to be updated too */\r
2850 VOID\r
2851 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2852 {\r
2853   HBITMAP oldBitmap;\r
2854   HBRUSH oldBrush;\r
2855   int tmpSize;\r
2856 \r
2857   if (appData.blindfold) return;\r
2858 \r
2859   /* [AS] Use font-based pieces if needed */\r
2860   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2861     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2862     CreatePiecesFromFont();\r
2863 \r
2864     if( fontBitmapSquareSize == squareSize ) {\r
2865         int index = TranslatePieceToFontPiece(piece);\r
2866 \r
2867         SelectObject( tmphdc, hPieceMask[ index ] );\r
2868 \r
2869       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2870         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2871       else\r
2872         BitBlt( hdc,\r
2873             x, y,\r
2874             squareSize, squareSize,\r
2875             tmphdc,\r
2876             0, 0,\r
2877             SRCAND );\r
2878 \r
2879         SelectObject( tmphdc, hPieceFace[ index ] );\r
2880 \r
2881       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2882         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2883       else\r
2884         BitBlt( hdc,\r
2885             x, y,\r
2886             squareSize, squareSize,\r
2887             tmphdc,\r
2888             0, 0,\r
2889             SRCPAINT );\r
2890 \r
2891         return;\r
2892     }\r
2893   }\r
2894 \r
2895   if (appData.monoMode) {\r
2896     SelectObject(tmphdc, PieceBitmap(piece, \r
2897       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2898     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2899            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2900   } else {\r
2901     tmpSize = squareSize;\r
2902     if(minorSize &&\r
2903         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2904          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2905       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2906       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2907       x += (squareSize - minorSize)>>1;\r
2908       y += squareSize - minorSize - 2;\r
2909       tmpSize = minorSize;\r
2910     }\r
2911     if (color || appData.allWhite ) {\r
2912       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2913       if( color )\r
2914               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2915       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2916       if(appData.upsideDown && color==flipView)\r
2917         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2918       else\r
2919         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2920       /* Use black for outline of white pieces */\r
2921       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2922       if(appData.upsideDown && color==flipView)\r
2923         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2924       else\r
2925         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2926     } else {\r
2927       /* Use square color for details of black pieces */\r
2928       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2929       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2930       if(appData.upsideDown && !flipView)\r
2931         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2932       else\r
2933         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2934     }\r
2935     SelectObject(hdc, oldBrush);\r
2936     SelectObject(tmphdc, oldBitmap);\r
2937   }\r
2938 }\r
2939 \r
2940 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2941 int GetBackTextureMode( int algo )\r
2942 {\r
2943     int result = BACK_TEXTURE_MODE_DISABLED;\r
2944 \r
2945     switch( algo ) \r
2946     {\r
2947         case BACK_TEXTURE_MODE_PLAIN:\r
2948             result = 1; /* Always use identity map */\r
2949             break;\r
2950         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2951             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2952             break;\r
2953     }\r
2954 \r
2955     return result;\r
2956 }\r
2957 \r
2958 /* \r
2959     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2960     to handle redraws cleanly (as random numbers would always be different).\r
2961 */\r
2962 VOID RebuildTextureSquareInfo()\r
2963 {\r
2964     BITMAP bi;\r
2965     int lite_w = 0;\r
2966     int lite_h = 0;\r
2967     int dark_w = 0;\r
2968     int dark_h = 0;\r
2969     int row;\r
2970     int col;\r
2971 \r
2972     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2973 \r
2974     if( liteBackTexture != NULL ) {\r
2975         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2976             lite_w = bi.bmWidth;\r
2977             lite_h = bi.bmHeight;\r
2978         }\r
2979     }\r
2980 \r
2981     if( darkBackTexture != NULL ) {\r
2982         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2983             dark_w = bi.bmWidth;\r
2984             dark_h = bi.bmHeight;\r
2985         }\r
2986     }\r
2987 \r
2988     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2989         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2990             if( (col + row) & 1 ) {\r
2991                 /* Lite square */\r
2992                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2993                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2994                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2995                   else\r
2996                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2997                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2998                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2999                   else\r
3000                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3001                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3002                 }\r
3003             }\r
3004             else {\r
3005                 /* Dark square */\r
3006                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3007                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3008                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3009                   else\r
3010                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3011                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3012                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3013                   else\r
3014                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3015                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3016                 }\r
3017             }\r
3018         }\r
3019     }\r
3020 }\r
3021 \r
3022 /* [AS] Arrow highlighting support */\r
3023 \r
3024 static double A_WIDTH = 5; /* Width of arrow body */\r
3025 \r
3026 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3027 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3028 \r
3029 static double Sqr( double x )\r
3030 {\r
3031     return x*x;\r
3032 }\r
3033 \r
3034 static int Round( double x )\r
3035 {\r
3036     return (int) (x + 0.5);\r
3037 }\r
3038 \r
3039 /* Draw an arrow between two points using current settings */\r
3040 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3041 {\r
3042     POINT arrow[7];\r
3043     double dx, dy, j, k, x, y;\r
3044 \r
3045     if( d_x == s_x ) {\r
3046         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3047 \r
3048         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3049         arrow[0].y = s_y;\r
3050 \r
3051         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3052         arrow[1].y = d_y - h;\r
3053 \r
3054         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3055         arrow[2].y = d_y - h;\r
3056 \r
3057         arrow[3].x = d_x;\r
3058         arrow[3].y = d_y;\r
3059 \r
3060         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3061         arrow[5].y = d_y - h;\r
3062 \r
3063         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3064         arrow[4].y = d_y - h;\r
3065 \r
3066         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3067         arrow[6].y = s_y;\r
3068     }\r
3069     else if( d_y == s_y ) {\r
3070         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3071 \r
3072         arrow[0].x = s_x;\r
3073         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3074 \r
3075         arrow[1].x = d_x - w;\r
3076         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3077 \r
3078         arrow[2].x = d_x - w;\r
3079         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3080 \r
3081         arrow[3].x = d_x;\r
3082         arrow[3].y = d_y;\r
3083 \r
3084         arrow[5].x = d_x - w;\r
3085         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3086 \r
3087         arrow[4].x = d_x - w;\r
3088         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3089 \r
3090         arrow[6].x = s_x;\r
3091         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3092     }\r
3093     else {\r
3094         /* [AS] Needed a lot of paper for this! :-) */\r
3095         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3096         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3097   \r
3098         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3099 \r
3100         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3101 \r
3102         x = s_x;\r
3103         y = s_y;\r
3104 \r
3105         arrow[0].x = Round(x - j);\r
3106         arrow[0].y = Round(y + j*dx);\r
3107 \r
3108         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3109         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3110 \r
3111         if( d_x > s_x ) {\r
3112             x = (double) d_x - k;\r
3113             y = (double) d_y - k*dy;\r
3114         }\r
3115         else {\r
3116             x = (double) d_x + k;\r
3117             y = (double) d_y + k*dy;\r
3118         }\r
3119 \r
3120         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3121 \r
3122         arrow[6].x = Round(x - j);\r
3123         arrow[6].y = Round(y + j*dx);\r
3124 \r
3125         arrow[2].x = Round(arrow[6].x + 2*j);\r
3126         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3127 \r
3128         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3129         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3130 \r
3131         arrow[4].x = d_x;\r
3132         arrow[4].y = d_y;\r
3133 \r
3134         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3135         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3136     }\r
3137 \r
3138     Polygon( hdc, arrow, 7 );\r
3139 }\r
3140 \r
3141 /* [AS] Draw an arrow between two squares */\r
3142 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3143 {\r
3144     int s_x, s_y, d_x, d_y;\r
3145     HPEN hpen;\r
3146     HPEN holdpen;\r
3147     HBRUSH hbrush;\r
3148     HBRUSH holdbrush;\r
3149     LOGBRUSH stLB;\r
3150 \r
3151     if( s_col == d_col && s_row == d_row ) {\r
3152         return;\r
3153     }\r
3154 \r
3155     /* Get source and destination points */\r
3156     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3157     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3158 \r
3159     if( d_y > s_y ) {\r
3160         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3161     }\r
3162     else if( d_y < s_y ) {\r
3163         d_y += squareSize / 2 + squareSize / 4;\r
3164     }\r
3165     else {\r
3166         d_y += squareSize / 2;\r
3167     }\r
3168 \r
3169     if( d_x > s_x ) {\r
3170         d_x += squareSize / 2 - squareSize / 4;\r
3171     }\r
3172     else if( d_x < s_x ) {\r
3173         d_x += squareSize / 2 + squareSize / 4;\r
3174     }\r
3175     else {\r
3176         d_x += squareSize / 2;\r
3177     }\r
3178 \r
3179     s_x += squareSize / 2;\r
3180     s_y += squareSize / 2;\r
3181 \r
3182     /* Adjust width */\r
3183     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3184 \r
3185     /* Draw */\r
3186     stLB.lbStyle = BS_SOLID;\r
3187     stLB.lbColor = appData.highlightArrowColor;\r
3188     stLB.lbHatch = 0;\r
3189 \r
3190     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3191     holdpen = SelectObject( hdc, hpen );\r
3192     hbrush = CreateBrushIndirect( &stLB );\r
3193     holdbrush = SelectObject( hdc, hbrush );\r
3194 \r
3195     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3196 \r
3197     SelectObject( hdc, holdpen );\r
3198     SelectObject( hdc, holdbrush );\r
3199     DeleteObject( hpen );\r
3200     DeleteObject( hbrush );\r
3201 }\r
3202 \r
3203 BOOL HasHighlightInfo()\r
3204 {\r
3205     BOOL result = FALSE;\r
3206 \r
3207     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3208         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3209     {\r
3210         result = TRUE;\r
3211     }\r
3212 \r
3213     return result;\r
3214 }\r
3215 \r
3216 BOOL IsDrawArrowEnabled()\r
3217 {\r
3218     BOOL result = FALSE;\r
3219 \r
3220     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3221         result = TRUE;\r
3222     }\r
3223 \r
3224     return result;\r
3225 }\r
3226 \r
3227 VOID DrawArrowHighlight( HDC hdc )\r
3228 {\r
3229     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3230         DrawArrowBetweenSquares( hdc,\r
3231             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3232             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3233     }\r
3234 }\r
3235 \r
3236 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3237 {\r
3238     HRGN result = NULL;\r
3239 \r
3240     if( HasHighlightInfo() ) {\r
3241         int x1, y1, x2, y2;\r
3242         int sx, sy, dx, dy;\r
3243 \r
3244         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3245         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3246 \r
3247         sx = MIN( x1, x2 );\r
3248         sy = MIN( y1, y2 );\r
3249         dx = MAX( x1, x2 ) + squareSize;\r
3250         dy = MAX( y1, y2 ) + squareSize;\r
3251 \r
3252         result = CreateRectRgn( sx, sy, dx, dy );\r
3253     }\r
3254 \r
3255     return result;\r
3256 }\r
3257 \r
3258 /*\r
3259     Warning: this function modifies the behavior of several other functions. \r
3260     \r
3261     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3262     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3263     repaint is scattered all over the place, which is not good for features such as\r
3264     "arrow highlighting" that require a full repaint of the board.\r
3265 \r
3266     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3267     user interaction, when speed is not so important) but especially to avoid errors\r
3268     in the displayed graphics.\r
3269 \r
3270     In such patched places, I always try refer to this function so there is a single\r
3271     place to maintain knowledge.\r
3272     \r
3273     To restore the original behavior, just return FALSE unconditionally.\r
3274 */\r
3275 BOOL IsFullRepaintPreferrable()\r
3276 {\r
3277     BOOL result = FALSE;\r
3278 \r
3279     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3280         /* Arrow may appear on the board */\r
3281         result = TRUE;\r
3282     }\r
3283 \r
3284     return result;\r
3285 }\r
3286 \r
3287 /* \r
3288     This function is called by DrawPosition to know whether a full repaint must\r
3289     be forced or not.\r
3290 \r
3291     Only DrawPosition may directly call this function, which makes use of \r
3292     some state information. Other function should call DrawPosition specifying \r
3293     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3294 */\r
3295 BOOL DrawPositionNeedsFullRepaint()\r
3296 {\r
3297     BOOL result = FALSE;\r
3298 \r
3299     /* \r
3300         Probably a slightly better policy would be to trigger a full repaint\r
3301         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3302         but animation is fast enough that it's difficult to notice.\r
3303     */\r
3304     if( animInfo.piece == EmptySquare ) {\r
3305         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3306             result = TRUE;\r
3307         }\r
3308     }\r
3309 \r
3310     return result;\r
3311 }\r
3312 \r
3313 VOID\r
3314 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3315 {\r
3316   int row, column, x, y, square_color, piece_color;\r
3317   ChessSquare piece;\r
3318   HBRUSH oldBrush;\r
3319   HDC texture_hdc = NULL;\r
3320 \r
3321   /* [AS] Initialize background textures if needed */\r
3322   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3323       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3324       if( backTextureSquareSize != squareSize \r
3325        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3326           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3327           backTextureSquareSize = squareSize;\r
3328           RebuildTextureSquareInfo();\r
3329       }\r
3330 \r
3331       texture_hdc = CreateCompatibleDC( hdc );\r
3332   }\r
3333 \r
3334   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3335     for (column = 0; column < BOARD_WIDTH; column++) {\r
3336   \r
3337       SquareToPos(row, column, &x, &y);\r
3338 \r
3339       piece = board[row][column];\r
3340 \r
3341       square_color = ((column + row) % 2) == 1;\r
3342       if( gameInfo.variant == VariantXiangqi ) {\r
3343           square_color = !InPalace(row, column);\r
3344           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3345           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3346       }\r
3347       piece_color = (int) piece < (int) BlackPawn;\r
3348 \r
3349 \r
3350       /* [HGM] holdings file: light square or black */\r
3351       if(column == BOARD_LEFT-2) {\r
3352             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3353                 square_color = 1;\r
3354             else {\r
3355                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3356                 continue;\r
3357             }\r
3358       } else\r
3359       if(column == BOARD_RGHT + 1 ) {\r
3360             if( row < gameInfo.holdingsSize )\r
3361                 square_color = 1;\r
3362             else {\r
3363                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3364                 continue;\r
3365             }\r
3366       }\r
3367       if(column == BOARD_LEFT-1 ) /* left align */\r
3368             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3369       else if( column == BOARD_RGHT) /* right align */\r
3370             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3371       else\r
3372       if (appData.monoMode) {\r
3373         if (piece == EmptySquare) {\r
3374           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3375                  square_color ? WHITENESS : BLACKNESS);\r
3376         } else {\r
3377           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3378         }\r
3379       } \r
3380       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3381           /* [AS] Draw the square using a texture bitmap */\r
3382           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3383           int r = row, c = column; // [HGM] do not flip board in flipView\r
3384           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3385 \r
3386           DrawTile( x, y, \r
3387               squareSize, squareSize, \r
3388               hdc, \r
3389               texture_hdc,\r
3390               backTextureSquareInfo[r][c].mode,\r
3391               backTextureSquareInfo[r][c].x,\r
3392               backTextureSquareInfo[r][c].y );\r
3393 \r
3394           SelectObject( texture_hdc, hbm );\r
3395 \r
3396           if (piece != EmptySquare) {\r
3397               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3398           }\r
3399       }\r
3400       else {\r
3401         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3402 \r
3403         oldBrush = SelectObject(hdc, brush );\r
3404         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3405         SelectObject(hdc, oldBrush);\r
3406         if (piece != EmptySquare)\r
3407           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3408       }\r
3409     }\r
3410   }\r
3411 \r
3412   if( texture_hdc != NULL ) {\r
3413     DeleteDC( texture_hdc );\r
3414   }\r
3415 }\r
3416 \r
3417 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3418 void fputDW(FILE *f, int x)\r
3419 {\r
3420         fputc(x     & 255, f);\r
3421         fputc(x>>8  & 255, f);\r
3422         fputc(x>>16 & 255, f);\r
3423         fputc(x>>24 & 255, f);\r
3424 }\r
3425 \r
3426 #define MAX_CLIPS 200   /* more than enough */\r
3427 \r
3428 VOID\r
3429 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3430 {\r
3431 //  HBITMAP bufferBitmap;\r
3432   BITMAP bi;\r
3433 //  RECT Rect;\r
3434   HDC tmphdc;\r
3435   HBITMAP hbm;\r
3436   int w = 100, h = 50;\r
3437 \r
3438   if(logo == NULL) {\r
3439     if(!logoHeight) return;\r
3440     FillRect( hdc, &logoRect, whitePieceBrush );\r
3441   }\r
3442 //  GetClientRect(hwndMain, &Rect);\r
3443 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3444 //                                      Rect.bottom-Rect.top+1);\r
3445   tmphdc = CreateCompatibleDC(hdc);\r
3446   hbm = SelectObject(tmphdc, logo);\r
3447   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3448             w = bi.bmWidth;\r
3449             h = bi.bmHeight;\r
3450   }\r
3451   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3452                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3453   SelectObject(tmphdc, hbm);\r
3454   DeleteDC(tmphdc);\r
3455 }\r
3456 \r
3457 VOID\r
3458 DisplayLogos()\r
3459 {\r
3460   if(logoHeight) {\r
3461         HDC hdc = GetDC(hwndMain);\r
3462         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3463         if(appData.autoLogo) {\r
3464           \r
3465           switch(gameMode) { // pick logos based on game mode\r
3466             case IcsObserving:\r
3467                 whiteLogo = second.programLogo; // ICS logo\r
3468                 blackLogo = second.programLogo;\r
3469             default:\r
3470                 break;\r
3471             case IcsPlayingWhite:\r
3472                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3473                 blackLogo = second.programLogo; // ICS logo\r
3474                 break;\r
3475             case IcsPlayingBlack:\r
3476                 whiteLogo = second.programLogo; // ICS logo\r
3477                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3478                 break;\r
3479             case TwoMachinesPlay:\r
3480                 if(first.twoMachinesColor[0] == 'b') {\r
3481                     whiteLogo = second.programLogo;\r
3482                     blackLogo = first.programLogo;\r
3483                 }\r
3484                 break;\r
3485             case MachinePlaysWhite:\r
3486                 blackLogo = userLogo;\r
3487                 break;\r
3488             case MachinePlaysBlack:\r
3489                 whiteLogo = userLogo;\r
3490                 blackLogo = first.programLogo;\r
3491           }\r
3492         }\r
3493         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3494         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3495         ReleaseDC(hwndMain, hdc);\r
3496   }\r
3497 }\r
3498 \r
3499 void\r
3500 UpdateLogos(int display)\r
3501 { // called after loading new engine(s), in tourney or from menu\r
3502   LoadLogo(&first, 0, FALSE);\r
3503   LoadLogo(&second, 1, appData.icsActive);\r
3504   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3505   if(display) DisplayLogos();\r
3506 }\r
3507 \r
3508 static HDC hdcSeek;\r
3509 \r
3510 // [HGM] seekgraph\r
3511 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3512 {\r
3513     POINT stPt;\r
3514     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3515     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3516     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3517     SelectObject( hdcSeek, hp );\r
3518 }\r
3519 \r
3520 // front-end wrapper for drawing functions to do rectangles\r
3521 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3522 {\r
3523     HPEN hp;\r
3524     RECT rc;\r
3525 \r
3526     if (hdcSeek == NULL) {\r
3527     hdcSeek = GetDC(hwndMain);\r
3528       if (!appData.monoMode) {\r
3529         SelectPalette(hdcSeek, hPal, FALSE);\r
3530         RealizePalette(hdcSeek);\r
3531       }\r
3532     }\r
3533     hp = SelectObject( hdcSeek, gridPen );\r
3534     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3535     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3536     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3537     SelectObject( hdcSeek, hp );\r
3538 }\r
3539 \r
3540 // front-end wrapper for putting text in graph\r
3541 void DrawSeekText(char *buf, int x, int y)\r
3542 {\r
3543         SIZE stSize;\r
3544         SetBkMode( hdcSeek, TRANSPARENT );\r
3545         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3546         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3547 }\r
3548 \r
3549 void DrawSeekDot(int x, int y, int color)\r
3550 {\r
3551         int square = color & 0x80;\r
3552         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3553                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3554         color &= 0x7F;\r
3555         if(square)\r
3556             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3557                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3558         else\r
3559             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3560                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3561             SelectObject(hdcSeek, oldBrush);\r
3562 }\r
3563 \r
3564 void DrawSeekOpen()\r
3565 {\r
3566 }\r
3567 \r
3568 void DrawSeekClose()\r
3569 {\r
3570 }\r
3571 \r
3572 VOID\r
3573 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3574 {\r
3575   static Board lastReq[2], lastDrawn[2];\r
3576   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3577   static int lastDrawnFlipView = 0;\r
3578   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3579   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3580   HDC tmphdc;\r
3581   HDC hdcmem;\r
3582   HBITMAP bufferBitmap;\r
3583   HBITMAP oldBitmap;\r
3584   RECT Rect;\r
3585   HRGN clips[MAX_CLIPS];\r
3586   ChessSquare dragged_piece = EmptySquare;\r
3587   int nr = twoBoards*partnerUp;\r
3588 \r
3589   /* I'm undecided on this - this function figures out whether a full\r
3590    * repaint is necessary on its own, so there's no real reason to have the\r
3591    * caller tell it that.  I think this can safely be set to FALSE - but\r
3592    * if we trust the callers not to request full repaints unnessesarily, then\r
3593    * we could skip some clipping work.  In other words, only request a full\r
3594    * redraw when the majority of pieces have changed positions (ie. flip, \r
3595    * gamestart and similar)  --Hawk\r
3596    */\r
3597   Boolean fullrepaint = repaint;\r
3598 \r
3599   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3600 \r
3601   if( DrawPositionNeedsFullRepaint() ) {\r
3602       fullrepaint = TRUE;\r
3603   }\r
3604 \r
3605   if (board == NULL) {\r
3606     if (!lastReqValid[nr]) {\r
3607       return;\r
3608     }\r
3609     board = lastReq[nr];\r
3610   } else {\r
3611     CopyBoard(lastReq[nr], board);\r
3612     lastReqValid[nr] = 1;\r
3613   }\r
3614 \r
3615   if (doingSizing) {\r
3616     return;\r
3617   }\r
3618 \r
3619   if (IsIconic(hwndMain)) {\r
3620     return;\r
3621   }\r
3622 \r
3623   if (hdc == NULL) {\r
3624     hdc = GetDC(hwndMain);\r
3625     if (!appData.monoMode) {\r
3626       SelectPalette(hdc, hPal, FALSE);\r
3627       RealizePalette(hdc);\r
3628     }\r
3629     releaseDC = TRUE;\r
3630   } else {\r
3631     releaseDC = FALSE;\r
3632   }\r
3633 \r
3634   /* Create some work-DCs */\r
3635   hdcmem = CreateCompatibleDC(hdc);\r
3636   tmphdc = CreateCompatibleDC(hdc);\r
3637 \r
3638   /* If dragging is in progress, we temporarely remove the piece */\r
3639   /* [HGM] or temporarily decrease count if stacked              */\r
3640   /*       !! Moved to before board compare !!                   */\r
3641   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3642     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3643     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3644             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3645         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3646     } else \r
3647     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3648             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3649         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3650     } else \r
3651         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3652   }\r
3653 \r
3654   /* Figure out which squares need updating by comparing the \r
3655    * newest board with the last drawn board and checking if\r
3656    * flipping has changed.\r
3657    */\r
3658   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3659     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3660       for (column = 0; column < BOARD_WIDTH; column++) {\r
3661         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3662           SquareToPos(row, column, &x, &y);\r
3663           clips[num_clips++] =\r
3664             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3665         }\r
3666       }\r
3667     }\r
3668    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3669     for (i=0; i<2; i++) {\r
3670       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3671           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3672         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3673             lastDrawnHighlight.sq[i].y >= 0) {\r
3674           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3675                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3676           clips[num_clips++] =\r
3677             CreateRectRgn(x - lineGap, y - lineGap, \r
3678                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3679         }\r
3680         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3681           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3682           clips[num_clips++] =\r
3683             CreateRectRgn(x - lineGap, y - lineGap, \r
3684                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3685         }\r
3686       }\r
3687     }\r
3688     for (i=0; i<2; i++) {\r
3689       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3690           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3691         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3692             lastDrawnPremove.sq[i].y >= 0) {\r
3693           SquareToPos(lastDrawnPremove.sq[i].y,\r
3694                       lastDrawnPremove.sq[i].x, &x, &y);\r
3695           clips[num_clips++] =\r
3696             CreateRectRgn(x - lineGap, y - lineGap, \r
3697                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3698         }\r
3699         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3700             premoveHighlightInfo.sq[i].y >= 0) {\r
3701           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3702                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3703           clips[num_clips++] =\r
3704             CreateRectRgn(x - lineGap, y - lineGap, \r
3705                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3706         }\r
3707       }\r
3708     }\r
3709    } else { // nr == 1\r
3710         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3711         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3712         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3713         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3714       for (i=0; i<2; i++) {\r
3715         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3716             partnerHighlightInfo.sq[i].y >= 0) {\r
3717           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3718                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3719           clips[num_clips++] =\r
3720             CreateRectRgn(x - lineGap, y - lineGap, \r
3721                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3722         }\r
3723         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3724             oldPartnerHighlight.sq[i].y >= 0) {\r
3725           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3726                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3727           clips[num_clips++] =\r
3728             CreateRectRgn(x - lineGap, y - lineGap, \r
3729                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3730         }\r
3731       }\r
3732    }\r
3733   } else {\r
3734     fullrepaint = TRUE;\r
3735   }\r
3736 \r
3737   /* Create a buffer bitmap - this is the actual bitmap\r
3738    * being written to.  When all the work is done, we can\r
3739    * copy it to the real DC (the screen).  This avoids\r
3740    * the problems with flickering.\r
3741    */\r
3742   GetClientRect(hwndMain, &Rect);\r
3743   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3744                                         Rect.bottom-Rect.top+1);\r
3745   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3746   if (!appData.monoMode) {\r
3747     SelectPalette(hdcmem, hPal, FALSE);\r
3748   }\r
3749 \r
3750   /* Create clips for dragging */\r
3751   if (!fullrepaint) {\r
3752     if (dragInfo.from.x >= 0) {\r
3753       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3754       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3755     }\r
3756     if (dragInfo.start.x >= 0) {\r
3757       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3758       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3759     }\r
3760     if (dragInfo.pos.x >= 0) {\r
3761       x = dragInfo.pos.x - squareSize / 2;\r
3762       y = dragInfo.pos.y - squareSize / 2;\r
3763       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3764     }\r
3765     if (dragInfo.lastpos.x >= 0) {\r
3766       x = dragInfo.lastpos.x - squareSize / 2;\r
3767       y = dragInfo.lastpos.y - squareSize / 2;\r
3768       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3769     }\r
3770   }\r
3771 \r
3772   /* Are we animating a move?  \r
3773    * If so, \r
3774    *   - remove the piece from the board (temporarely)\r
3775    *   - calculate the clipping region\r
3776    */\r
3777   if (!fullrepaint) {\r
3778     if (animInfo.piece != EmptySquare) {\r
3779       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3780       x = boardRect.left + animInfo.lastpos.x;\r
3781       y = boardRect.top + animInfo.lastpos.y;\r
3782       x2 = boardRect.left + animInfo.pos.x;\r
3783       y2 = boardRect.top + animInfo.pos.y;\r
3784       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3785       /* Slight kludge.  The real problem is that after AnimateMove is\r
3786          done, the position on the screen does not match lastDrawn.\r
3787          This currently causes trouble only on e.p. captures in\r
3788          atomic, where the piece moves to an empty square and then\r
3789          explodes.  The old and new positions both had an empty square\r
3790          at the destination, but animation has drawn a piece there and\r
3791          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3792       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3793     }\r
3794   }\r
3795 \r
3796   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3797   if (num_clips == 0)\r
3798     fullrepaint = TRUE;\r
3799 \r
3800   /* Set clipping on the memory DC */\r
3801   if (!fullrepaint) {\r
3802     SelectClipRgn(hdcmem, clips[0]);\r
3803     for (x = 1; x < num_clips; x++) {\r
3804       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3805         abort();  // this should never ever happen!\r
3806     }\r
3807   }\r
3808 \r
3809   /* Do all the drawing to the memory DC */\r
3810   if(explodeInfo.radius) { // [HGM] atomic\r
3811         HBRUSH oldBrush;\r
3812         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3813         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3814         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3815         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3816         x += squareSize/2;\r
3817         y += squareSize/2;\r
3818         if(!fullrepaint) {\r
3819           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3820           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3821         }\r
3822         DrawGridOnDC(hdcmem);\r
3823         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3824         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3825         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3826         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3827         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3828         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3829         SelectObject(hdcmem, oldBrush);\r
3830   } else {\r
3831     DrawGridOnDC(hdcmem);\r
3832     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3833         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3834         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3835     } else {\r
3836         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3837         oldPartnerHighlight = partnerHighlightInfo;\r
3838     }\r
3839     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3840   }\r
3841   if(nr == 0) // [HGM] dual: markers only on left board\r
3842   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3843     for (column = 0; column < BOARD_WIDTH; column++) {\r
3844         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3845             HBRUSH oldBrush = SelectObject(hdcmem, \r
3846                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3847             SquareToPos(row, column, &x, &y);\r
3848             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3849                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3850             SelectObject(hdcmem, oldBrush);\r
3851         }\r
3852     }\r
3853   }\r
3854 \r
3855   if( appData.highlightMoveWithArrow ) {\r
3856     DrawArrowHighlight(hdcmem);\r
3857   }\r
3858 \r
3859   DrawCoordsOnDC(hdcmem);\r
3860 \r
3861   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3862                  /* to make sure lastDrawn contains what is actually drawn */\r
3863 \r
3864   /* Put the dragged piece back into place and draw it (out of place!) */\r
3865     if (dragged_piece != EmptySquare) {\r
3866     /* [HGM] or restack */\r
3867     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3868                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3869     else\r
3870     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3871                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3872     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3873     x = dragInfo.pos.x - squareSize / 2;\r
3874     y = dragInfo.pos.y - squareSize / 2;\r
3875     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3876                   ((int) dragInfo.piece < (int) BlackPawn), \r
3877                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3878   }   \r
3879   \r
3880   /* Put the animated piece back into place and draw it */\r
3881   if (animInfo.piece != EmptySquare) {\r
3882     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3883     x = boardRect.left + animInfo.pos.x;\r
3884     y = boardRect.top + animInfo.pos.y;\r
3885     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3886                   ((int) animInfo.piece < (int) BlackPawn),\r
3887                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3888   }\r
3889 \r
3890   /* Release the bufferBitmap by selecting in the old bitmap \r
3891    * and delete the memory DC\r
3892    */\r
3893   SelectObject(hdcmem, oldBitmap);\r
3894   DeleteDC(hdcmem);\r
3895 \r
3896   /* Set clipping on the target DC */\r
3897   if (!fullrepaint) {\r
3898     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3899         RECT rect;\r
3900         GetRgnBox(clips[x], &rect);\r
3901         DeleteObject(clips[x]);\r
3902         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3903                           rect.right + wpMain.width/2, rect.bottom);\r
3904     }\r
3905     SelectClipRgn(hdc, clips[0]);\r
3906     for (x = 1; x < num_clips; x++) {\r
3907       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3908         abort();   // this should never ever happen!\r
3909     } \r
3910   }\r
3911 \r
3912   /* Copy the new bitmap onto the screen in one go.\r
3913    * This way we avoid any flickering\r
3914    */\r
3915   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3916   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3917          boardRect.right - boardRect.left,\r
3918          boardRect.bottom - boardRect.top,\r
3919          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3920   if(saveDiagFlag) { \r
3921     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3922     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3923 \r
3924     GetObject(bufferBitmap, sizeof(b), &b);\r
3925     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3926         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3927         bih.biWidth = b.bmWidth;\r
3928         bih.biHeight = b.bmHeight;\r
3929         bih.biPlanes = 1;\r
3930         bih.biBitCount = b.bmBitsPixel;\r
3931         bih.biCompression = 0;\r
3932         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3933         bih.biXPelsPerMeter = 0;\r
3934         bih.biYPelsPerMeter = 0;\r
3935         bih.biClrUsed = 0;\r
3936         bih.biClrImportant = 0;\r
3937 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3938 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3939         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3940 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3941 \r
3942         wb = b.bmWidthBytes;\r
3943         // count colors\r
3944         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3945                 int k = ((int*) pData)[i];\r
3946                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3947                 if(j >= 16) break;\r
3948                 color[j] = k;\r
3949                 if(j >= nrColors) nrColors = j+1;\r
3950         }\r
3951         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3952                 INT p = 0;\r
3953                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3954                     for(w=0; w<(wb>>2); w+=2) {\r
3955                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3956                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3957                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3958                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3959                         pData[p++] = m | j<<4;\r
3960                     }\r
3961                     while(p&3) pData[p++] = 0;\r
3962                 }\r
3963                 fac = 3;\r
3964                 wb = ((wb+31)>>5)<<2;\r
3965         }\r
3966         // write BITMAPFILEHEADER\r
3967         fprintf(diagFile, "BM");\r
3968         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3969         fputDW(diagFile, 0);\r
3970         fputDW(diagFile, 0x36 + (fac?64:0));\r
3971         // write BITMAPINFOHEADER\r
3972         fputDW(diagFile, 40);\r
3973         fputDW(diagFile, b.bmWidth);\r
3974         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3975         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3976         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3977         fputDW(diagFile, 0);\r
3978         fputDW(diagFile, 0);\r
3979         fputDW(diagFile, 0);\r
3980         fputDW(diagFile, 0);\r
3981         fputDW(diagFile, 0);\r
3982         fputDW(diagFile, 0);\r
3983         // write color table\r
3984         if(fac)\r
3985         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3986         // write bitmap data\r
3987         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3988                 fputc(pData[i], diagFile);\r
3989         free(pData);\r
3990      }\r
3991   }\r
3992 \r
3993   SelectObject(tmphdc, oldBitmap);\r
3994 \r
3995   /* Massive cleanup */\r
3996   for (x = 0; x < num_clips; x++)\r
3997     DeleteObject(clips[x]);\r
3998 \r
3999   DeleteDC(tmphdc);\r
4000   DeleteObject(bufferBitmap);\r
4001 \r
4002   if (releaseDC) \r
4003     ReleaseDC(hwndMain, hdc);\r
4004   \r
4005   if (lastDrawnFlipView != flipView && nr == 0) {\r
4006     if (flipView)\r
4007       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4008     else\r
4009       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4010   }\r
4011 \r
4012 /*  CopyBoard(lastDrawn, board);*/\r
4013   lastDrawnHighlight = highlightInfo;\r
4014   lastDrawnPremove   = premoveHighlightInfo;\r
4015   lastDrawnFlipView = flipView;\r
4016   lastDrawnValid[nr] = 1;\r
4017 }\r
4018 \r
4019 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4020 int\r
4021 SaveDiagram(f)\r
4022      FILE *f;\r
4023 {\r
4024     saveDiagFlag = 1; diagFile = f;\r
4025     HDCDrawPosition(NULL, TRUE, NULL);\r
4026     saveDiagFlag = 0;\r
4027 \r
4028     fclose(f);\r
4029     return TRUE;\r
4030 }\r
4031 \r
4032 \r
4033 /*---------------------------------------------------------------------------*\\r
4034 | CLIENT PAINT PROCEDURE\r
4035 |   This is the main event-handler for the WM_PAINT message.\r
4036 |\r
4037 \*---------------------------------------------------------------------------*/\r
4038 VOID\r
4039 PaintProc(HWND hwnd)\r
4040 {\r
4041   HDC         hdc;\r
4042   PAINTSTRUCT ps;\r
4043   HFONT       oldFont;\r
4044 \r
4045   if((hdc = BeginPaint(hwnd, &ps))) {\r
4046     if (IsIconic(hwnd)) {\r
4047       DrawIcon(hdc, 2, 2, iconCurrent);\r
4048     } else {\r
4049       if (!appData.monoMode) {\r
4050         SelectPalette(hdc, hPal, FALSE);\r
4051         RealizePalette(hdc);\r
4052       }\r
4053       HDCDrawPosition(hdc, 1, NULL);\r
4054       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4055         flipView = !flipView; partnerUp = !partnerUp;\r
4056         HDCDrawPosition(hdc, 1, NULL);\r
4057         flipView = !flipView; partnerUp = !partnerUp;\r
4058       }\r
4059       oldFont =\r
4060         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4061       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4062                  ETO_CLIPPED|ETO_OPAQUE,\r
4063                  &messageRect, messageText, strlen(messageText), NULL);\r
4064       SelectObject(hdc, oldFont);\r
4065       DisplayBothClocks();\r
4066       DisplayLogos();\r
4067     }\r
4068     EndPaint(hwnd,&ps);\r
4069   }\r
4070 \r
4071   return;\r
4072 }\r
4073 \r
4074 \r
4075 /*\r
4076  * If the user selects on a border boundary, return -1; if off the board,\r
4077  *   return -2.  Otherwise map the event coordinate to the square.\r
4078  * The offset boardRect.left or boardRect.top must already have been\r
4079  *   subtracted from x.\r
4080  */\r
4081 int EventToSquare(x, limit)\r
4082      int x, limit;\r
4083 {\r
4084   if (x <= 0)\r
4085     return -2;\r
4086   if (x < lineGap)\r
4087     return -1;\r
4088   x -= lineGap;\r
4089   if ((x % (squareSize + lineGap)) >= squareSize)\r
4090     return -1;\r
4091   x /= (squareSize + lineGap);\r
4092     if (x >= limit)\r
4093     return -2;\r
4094   return x;\r
4095 }\r
4096 \r
4097 typedef struct {\r
4098   char piece;\r
4099   int command;\r
4100   char* name;\r
4101 } DropEnable;\r
4102 \r
4103 DropEnable dropEnables[] = {\r
4104   { 'P', DP_Pawn, N_("Pawn") },\r
4105   { 'N', DP_Knight, N_("Knight") },\r
4106   { 'B', DP_Bishop, N_("Bishop") },\r
4107   { 'R', DP_Rook, N_("Rook") },\r
4108   { 'Q', DP_Queen, N_("Queen") },\r
4109 };\r
4110 \r
4111 VOID\r
4112 SetupDropMenu(HMENU hmenu)\r
4113 {\r
4114   int i, count, enable;\r
4115   char *p;\r
4116   extern char white_holding[], black_holding[];\r
4117   char item[MSG_SIZ];\r
4118 \r
4119   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4120     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4121                dropEnables[i].piece);\r
4122     count = 0;\r
4123     while (p && *p++ == dropEnables[i].piece) count++;\r
4124       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4125     enable = count > 0 || !appData.testLegality\r
4126       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4127                       && !appData.icsActive);\r
4128     ModifyMenu(hmenu, dropEnables[i].command,\r
4129                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4130                dropEnables[i].command, item);\r
4131   }\r
4132 }\r
4133 \r
4134 void DragPieceBegin(int x, int y, Boolean instantly)\r
4135 {\r
4136       dragInfo.lastpos.x = boardRect.left + x;\r
4137       dragInfo.lastpos.y = boardRect.top + y;\r
4138       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4139       dragInfo.from.x = fromX;\r
4140       dragInfo.from.y = fromY;\r
4141       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4142       dragInfo.start = dragInfo.from;\r
4143       SetCapture(hwndMain);\r
4144 }\r
4145 \r
4146 void DragPieceEnd(int x, int y)\r
4147 {\r
4148     ReleaseCapture();\r
4149     dragInfo.start.x = dragInfo.start.y = -1;\r
4150     dragInfo.from = dragInfo.start;\r
4151     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4152 }\r
4153 \r
4154 void ChangeDragPiece(ChessSquare piece)\r
4155 {\r
4156     dragInfo.piece = piece;\r
4157 }\r
4158 \r
4159 /* Event handler for mouse messages */\r
4160 VOID\r
4161 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4162 {\r
4163   int x, y, menuNr;\r
4164   POINT pt;\r
4165   static int recursive = 0;\r
4166   HMENU hmenu;\r
4167   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4168 \r
4169   if (recursive) {\r
4170     if (message == WM_MBUTTONUP) {\r
4171       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4172          to the middle button: we simulate pressing the left button too!\r
4173          */\r
4174       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4175       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4176     }\r
4177     return;\r
4178   }\r
4179   recursive++;\r
4180   \r
4181   pt.x = LOWORD(lParam);\r
4182   pt.y = HIWORD(lParam);\r
4183   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4184   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4185   if (!flipView && y >= 0) {\r
4186     y = BOARD_HEIGHT - 1 - y;\r
4187   }\r
4188   if (flipView && x >= 0) {\r
4189     x = BOARD_WIDTH - 1 - x;\r
4190   }\r
4191 \r
4192   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4193 \r
4194   switch (message) {\r
4195   case WM_LBUTTONDOWN:\r
4196       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4197         ClockClick(flipClock); break;\r
4198       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4199         ClockClick(!flipClock); break;\r
4200       }\r
4201       dragInfo.start.x = dragInfo.start.y = -1;\r
4202       dragInfo.from = dragInfo.start;\r
4203     if(fromX == -1 && frozen) { // not sure where this is for\r
4204                 fromX = fromY = -1; \r
4205       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4206       break;\r
4207     }\r
4208       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4209       DrawPosition(TRUE, NULL);\r
4210     break;\r
4211 \r
4212   case WM_LBUTTONUP:\r
4213       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4214       DrawPosition(TRUE, NULL);\r
4215     break;\r
4216 \r
4217   case WM_MOUSEMOVE:\r
4218     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4219     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4220     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4221     if ((appData.animateDragging || appData.highlightDragging)\r
4222         && (wParam & MK_LBUTTON)\r
4223         && dragInfo.from.x >= 0) \r
4224     {\r
4225       BOOL full_repaint = FALSE;\r
4226 \r
4227       if (appData.animateDragging) {\r
4228         dragInfo.pos = pt;\r
4229       }\r
4230       if (appData.highlightDragging) {\r
4231         SetHighlights(fromX, fromY, x, y);\r
4232         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4233             full_repaint = TRUE;\r
4234         }\r
4235       }\r
4236       \r
4237       DrawPosition( full_repaint, NULL);\r
4238       \r
4239       dragInfo.lastpos = dragInfo.pos;\r
4240     }\r
4241     break;\r
4242 \r
4243   case WM_MOUSEWHEEL: // [DM]\r
4244     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4245        /* Mouse Wheel is being rolled forward\r
4246         * Play moves forward\r
4247         */\r
4248        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4249                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4250        /* Mouse Wheel is being rolled backward\r
4251         * Play moves backward\r
4252         */\r
4253        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4254                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4255     }\r
4256     break;\r
4257 \r
4258   case WM_MBUTTONUP:\r
4259   case WM_RBUTTONUP:\r
4260     ReleaseCapture();\r
4261     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4262     break;\r
4263  \r
4264   case WM_MBUTTONDOWN:\r
4265   case WM_RBUTTONDOWN:\r
4266     ErrorPopDown();\r
4267     ReleaseCapture();\r
4268     fromX = fromY = -1;\r
4269     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4270     dragInfo.start.x = dragInfo.start.y = -1;\r
4271     dragInfo.from = dragInfo.start;\r
4272     dragInfo.lastpos = dragInfo.pos;\r
4273     if (appData.highlightDragging) {\r
4274       ClearHighlights();\r
4275     }\r
4276     if(y == -2) {\r
4277       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4278       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4279           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4280       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4281           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4282       }\r
4283       break;\r
4284     }\r
4285     DrawPosition(TRUE, NULL);\r
4286 \r
4287     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4288     switch (menuNr) {\r
4289     case 0:\r
4290       if (message == WM_MBUTTONDOWN) {\r
4291         buttonCount = 3;  /* even if system didn't think so */\r
4292         if (wParam & MK_SHIFT) \r
4293           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4294         else\r
4295           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4296       } else { /* message == WM_RBUTTONDOWN */\r
4297         /* Just have one menu, on the right button.  Windows users don't\r
4298            think to try the middle one, and sometimes other software steals\r
4299            it, or it doesn't really exist. */\r
4300         if(gameInfo.variant != VariantShogi)\r
4301             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4302         else\r
4303             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4304       }\r
4305       break;\r
4306     case 2:\r
4307       SetCapture(hwndMain);\r
4308       break;\r
4309     case 1:\r
4310       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4311       SetupDropMenu(hmenu);\r
4312       MenuPopup(hwnd, pt, hmenu, -1);\r
4313     default:\r
4314       break;\r
4315     }\r
4316     break;\r
4317   }\r
4318 \r
4319   recursive--;\r
4320 }\r
4321 \r
4322 /* Preprocess messages for buttons in main window */\r
4323 LRESULT CALLBACK\r
4324 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4325 {\r
4326   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4327   int i, dir;\r
4328 \r
4329   for (i=0; i<N_BUTTONS; i++) {\r
4330     if (buttonDesc[i].id == id) break;\r
4331   }\r
4332   if (i == N_BUTTONS) return 0;\r
4333   switch (message) {\r
4334   case WM_KEYDOWN:\r
4335     switch (wParam) {\r
4336     case VK_LEFT:\r
4337     case VK_RIGHT:\r
4338       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4339       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4340       return TRUE;\r
4341     }\r
4342     break;\r
4343   case WM_CHAR:\r
4344     switch (wParam) {\r
4345     case '\r':\r
4346       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4347       return TRUE;\r
4348     default:\r
4349       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4350         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4351         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4352         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4353         SetFocus(h);\r
4354         SendMessage(h, WM_CHAR, wParam, lParam);\r
4355         return TRUE;\r
4356       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4357         TypeInEvent((char)wParam);\r
4358       }\r
4359       break;\r
4360     }\r
4361     break;\r
4362   }\r
4363   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4364 }\r
4365 \r
4366 /* Process messages for Promotion dialog box */\r
4367 LRESULT CALLBACK\r
4368 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4369 {\r
4370   char promoChar;\r
4371 \r
4372   switch (message) {\r
4373   case WM_INITDIALOG: /* message: initialize dialog box */\r
4374     /* Center the dialog over the application window */\r
4375     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4376     Translate(hDlg, DLG_PromotionKing);\r
4377     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4378       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4379        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4380        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4381                SW_SHOW : SW_HIDE);\r
4382     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4383     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4384        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4385          PieceToChar(WhiteAngel) != '~') ||\r
4386         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4387          PieceToChar(BlackAngel) != '~')   ) ?\r
4388                SW_SHOW : SW_HIDE);\r
4389     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4390        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4391          PieceToChar(WhiteMarshall) != '~') ||\r
4392         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4393          PieceToChar(BlackMarshall) != '~')   ) ?\r
4394                SW_SHOW : SW_HIDE);\r
4395     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4396     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4397        gameInfo.variant != VariantShogi ?\r
4398                SW_SHOW : SW_HIDE);\r
4399     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4400        gameInfo.variant != VariantShogi ?\r
4401                SW_SHOW : SW_HIDE);\r
4402     if(gameInfo.variant == VariantShogi) {\r
4403         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4404         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4405         SetWindowText(hDlg, "Promote?");\r
4406     }\r
4407     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4408        gameInfo.variant == VariantSuper ?\r
4409                SW_SHOW : SW_HIDE);\r
4410     return TRUE;\r
4411 \r
4412   case WM_COMMAND: /* message: received a command */\r
4413     switch (LOWORD(wParam)) {\r
4414     case IDCANCEL:\r
4415       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4416       ClearHighlights();\r
4417       DrawPosition(FALSE, NULL);\r
4418       return TRUE;\r
4419     case PB_King:\r
4420       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4421       break;\r
4422     case PB_Queen:\r
4423       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4424       break;\r
4425     case PB_Rook:\r
4426       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4427       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4428       break;\r
4429     case PB_Bishop:\r
4430       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4431       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4432       break;\r
4433     case PB_Chancellor:\r
4434       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4435       break;\r
4436     case PB_Archbishop:\r
4437       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4438       break;\r
4439     case PB_Knight:\r
4440       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4441       break;\r
4442     default:\r
4443       return FALSE;\r
4444     }\r
4445     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4446     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4447     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4448     fromX = fromY = -1;\r
4449     if (!appData.highlightLastMove) {\r
4450       ClearHighlights();\r
4451       DrawPosition(FALSE, NULL);\r
4452     }\r
4453     return TRUE;\r
4454   }\r
4455   return FALSE;\r
4456 }\r
4457 \r
4458 /* Pop up promotion dialog */\r
4459 VOID\r
4460 PromotionPopup(HWND hwnd)\r
4461 {\r
4462   FARPROC lpProc;\r
4463 \r
4464   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4465   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4466     hwnd, (DLGPROC)lpProc);\r
4467   FreeProcInstance(lpProc);\r
4468 }\r
4469 \r
4470 void\r
4471 PromotionPopUp()\r
4472 {\r
4473   DrawPosition(TRUE, NULL);\r
4474   PromotionPopup(hwndMain);\r
4475 }\r
4476 \r
4477 VOID\r
4478 LoadGameDialog(HWND hwnd, char* title)\r
4479 {\r
4480   UINT number = 0;\r
4481   FILE *f;\r
4482   char fileTitle[MSG_SIZ];\r
4483   f = OpenFileDialog(hwnd, "rb", "",\r
4484                      appData.oldSaveStyle ? "gam" : "pgn",\r
4485                      GAME_FILT,\r
4486                      title, &number, fileTitle, NULL);\r
4487   if (f != NULL) {\r
4488     cmailMsgLoaded = FALSE;\r
4489     if (number == 0) {\r
4490       int error = GameListBuild(f);\r
4491       if (error) {\r
4492         DisplayError(_("Cannot build game list"), error);\r
4493       } else if (!ListEmpty(&gameList) &&\r
4494                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4495         GameListPopUp(f, fileTitle);\r
4496         return;\r
4497       }\r
4498       GameListDestroy();\r
4499       number = 1;\r
4500     }\r
4501     LoadGame(f, number, fileTitle, FALSE);\r
4502   }\r
4503 }\r
4504 \r
4505 int get_term_width()\r
4506 {\r
4507     HDC hdc;\r
4508     TEXTMETRIC tm;\r
4509     RECT rc;\r
4510     HFONT hfont, hold_font;\r
4511     LOGFONT lf;\r
4512     HWND hText;\r
4513 \r
4514     if (hwndConsole)\r
4515         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4516     else\r
4517         return 79;\r
4518 \r
4519     // get the text metrics\r
4520     hdc = GetDC(hText);\r
4521     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4522     if (consoleCF.dwEffects & CFE_BOLD)\r
4523         lf.lfWeight = FW_BOLD;\r
4524     if (consoleCF.dwEffects & CFE_ITALIC)\r
4525         lf.lfItalic = TRUE;\r
4526     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4527         lf.lfStrikeOut = TRUE;\r
4528     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4529         lf.lfUnderline = TRUE;\r
4530     hfont = CreateFontIndirect(&lf);\r
4531     hold_font = SelectObject(hdc, hfont);\r
4532     GetTextMetrics(hdc, &tm);\r
4533     SelectObject(hdc, hold_font);\r
4534     DeleteObject(hfont);\r
4535     ReleaseDC(hText, hdc);\r
4536 \r
4537     // get the rectangle\r
4538     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4539 \r
4540     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4541 }\r
4542 \r
4543 void UpdateICSWidth(HWND hText)\r
4544 {\r
4545     LONG old_width, new_width;\r
4546 \r
4547     new_width = get_term_width(hText, FALSE);\r
4548     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4549     if (new_width != old_width)\r
4550     {\r
4551         ics_update_width(new_width);\r
4552         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4553     }\r
4554 }\r
4555 \r
4556 VOID\r
4557 ChangedConsoleFont()\r
4558 {\r
4559   CHARFORMAT cfmt;\r
4560   CHARRANGE tmpsel, sel;\r
4561   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4562   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4563   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4564   PARAFORMAT paraf;\r
4565 \r
4566   cfmt.cbSize = sizeof(CHARFORMAT);\r
4567   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4568     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4569                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4570   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4571    * size.  This was undocumented in the version of MSVC++ that I had\r
4572    * when I wrote the code, but is apparently documented now.\r
4573    */\r
4574   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4575   cfmt.bCharSet = f->lf.lfCharSet;\r
4576   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4577   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4578   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4579   /* Why are the following seemingly needed too? */\r
4580   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4581   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4582   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4583   tmpsel.cpMin = 0;\r
4584   tmpsel.cpMax = -1; /*999999?*/\r
4585   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4586   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4587   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4588    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4589    */\r
4590   paraf.cbSize = sizeof(paraf);\r
4591   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4592   paraf.dxStartIndent = 0;\r
4593   paraf.dxOffset = WRAP_INDENT;\r
4594   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4595   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4596   UpdateICSWidth(hText);\r
4597 }\r
4598 \r
4599 /*---------------------------------------------------------------------------*\\r
4600  *\r
4601  * Window Proc for main window\r
4602  *\r
4603 \*---------------------------------------------------------------------------*/\r
4604 \r
4605 /* Process messages for main window, etc. */\r
4606 LRESULT CALLBACK\r
4607 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4608 {\r
4609   FARPROC lpProc;\r
4610   int wmId, wmEvent;\r
4611   char *defName;\r
4612   FILE *f;\r
4613   UINT number;\r
4614   char fileTitle[MSG_SIZ];\r
4615   char buf[MSG_SIZ];\r
4616   static SnapData sd;\r
4617   static int peek=0;\r
4618 \r
4619   switch (message) {\r
4620 \r
4621   case WM_PAINT: /* message: repaint portion of window */\r
4622     PaintProc(hwnd);\r
4623     break;\r
4624 \r
4625   case WM_ERASEBKGND:\r
4626     if (IsIconic(hwnd)) {\r
4627       /* Cheat; change the message */\r
4628       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4629     } else {\r
4630       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4631     }\r
4632     break;\r
4633 \r
4634   case WM_LBUTTONDOWN:\r
4635   case WM_MBUTTONDOWN:\r
4636   case WM_RBUTTONDOWN:\r
4637   case WM_LBUTTONUP:\r
4638   case WM_MBUTTONUP:\r
4639   case WM_RBUTTONUP:\r
4640   case WM_MOUSEMOVE:\r
4641   case WM_MOUSEWHEEL:\r
4642     MouseEvent(hwnd, message, wParam, lParam);\r
4643     break;\r
4644 \r
4645   case WM_KEYUP:\r
4646     if((char)wParam == '\b') {\r
4647       ForwardEvent(); peek = 0;\r
4648     }\r
4649 \r
4650     JAWS_KBUP_NAVIGATION\r
4651 \r
4652     break;\r
4653 \r
4654   case WM_KEYDOWN:\r
4655     if((char)wParam == '\b') {\r
4656       if(!peek) BackwardEvent(), peek = 1;\r
4657     }\r
4658 \r
4659     JAWS_KBDOWN_NAVIGATION\r
4660 \r
4661     break;\r
4662 \r
4663   case WM_CHAR:\r
4664     \r
4665     JAWS_ALT_INTERCEPT\r
4666 \r
4667     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4668         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4669         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4670         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4671         SetFocus(h);\r
4672         SendMessage(h, message, wParam, lParam);\r
4673     } else if(lParam != KF_REPEAT) {\r
4674         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4675                 TypeInEvent((char)wParam);\r
4676         } else if((char)wParam == 003) CopyGameToClipboard();\r
4677          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4678     }\r
4679 \r
4680     break;\r
4681 \r
4682   case WM_PALETTECHANGED:\r
4683     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4684       int nnew;\r
4685       HDC hdc = GetDC(hwndMain);\r
4686       SelectPalette(hdc, hPal, TRUE);\r
4687       nnew = RealizePalette(hdc);\r
4688       if (nnew > 0) {\r
4689         paletteChanged = TRUE;\r
4690         InvalidateRect(hwnd, &boardRect, FALSE);\r
4691       }\r
4692       ReleaseDC(hwnd, hdc);\r
4693     }\r
4694     break;\r
4695 \r
4696   case WM_QUERYNEWPALETTE:\r
4697     if (!appData.monoMode /*&& paletteChanged*/) {\r
4698       int nnew;\r
4699       HDC hdc = GetDC(hwndMain);\r
4700       paletteChanged = FALSE;\r
4701       SelectPalette(hdc, hPal, FALSE);\r
4702       nnew = RealizePalette(hdc);\r
4703       if (nnew > 0) {\r
4704         InvalidateRect(hwnd, &boardRect, FALSE);\r
4705       }\r
4706       ReleaseDC(hwnd, hdc);\r
4707       return TRUE;\r
4708     }\r
4709     return FALSE;\r
4710 \r
4711   case WM_COMMAND: /* message: command from application menu */\r
4712     wmId    = LOWORD(wParam);\r
4713     wmEvent = HIWORD(wParam);\r
4714 \r
4715     switch (wmId) {\r
4716     case IDM_NewGame:\r
4717       ResetGameEvent();\r
4718       SAY("new game enter a move to play against the computer with white");\r
4719       break;\r
4720 \r
4721     case IDM_NewGameFRC:\r
4722       if( NewGameFRC() == 0 ) {\r
4723         ResetGameEvent();\r
4724       }\r
4725       break;\r
4726 \r
4727     case IDM_NewVariant:\r
4728       NewVariantPopup(hwnd);\r
4729       break;\r
4730 \r
4731     case IDM_LoadGame:\r
4732       LoadGameDialog(hwnd, _("Load Game from File"));\r
4733       break;\r
4734 \r
4735     case IDM_LoadNextGame:\r
4736       ReloadGame(1);\r
4737       break;\r
4738 \r
4739     case IDM_LoadPrevGame:\r
4740       ReloadGame(-1);\r
4741       break;\r
4742 \r
4743     case IDM_ReloadGame:\r
4744       ReloadGame(0);\r
4745       break;\r
4746 \r
4747     case IDM_LoadPosition:\r
4748       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4749         Reset(FALSE, TRUE);\r
4750       }\r
4751       number = 1;\r
4752       f = OpenFileDialog(hwnd, "rb", "",\r
4753                          appData.oldSaveStyle ? "pos" : "fen",\r
4754                          POSITION_FILT,\r
4755                          _("Load Position from File"), &number, fileTitle, NULL);\r
4756       if (f != NULL) {\r
4757         LoadPosition(f, number, fileTitle);\r
4758       }\r
4759       break;\r
4760 \r
4761     case IDM_LoadNextPosition:\r
4762       ReloadPosition(1);\r
4763       break;\r
4764 \r
4765     case IDM_LoadPrevPosition:\r
4766       ReloadPosition(-1);\r
4767       break;\r
4768 \r
4769     case IDM_ReloadPosition:\r
4770       ReloadPosition(0);\r
4771       break;\r
4772 \r
4773     case IDM_SaveGame:\r
4774       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4775       f = OpenFileDialog(hwnd, "a", defName,\r
4776                          appData.oldSaveStyle ? "gam" : "pgn",\r
4777                          GAME_FILT,\r
4778                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4779       if (f != NULL) {\r
4780         SaveGame(f, 0, "");\r
4781       }\r
4782       break;\r
4783 \r
4784     case IDM_SavePosition:\r
4785       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4786       f = OpenFileDialog(hwnd, "a", defName,\r
4787                          appData.oldSaveStyle ? "pos" : "fen",\r
4788                          POSITION_FILT,\r
4789                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4790       if (f != NULL) {\r
4791         SavePosition(f, 0, "");\r
4792       }\r
4793       break;\r
4794 \r
4795     case IDM_SaveDiagram:\r
4796       defName = "diagram";\r
4797       f = OpenFileDialog(hwnd, "wb", defName,\r
4798                          "bmp",\r
4799                          DIAGRAM_FILT,\r
4800                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4801       if (f != NULL) {\r
4802         SaveDiagram(f);\r
4803       }\r
4804       break;\r
4805 \r
4806     case IDM_CopyGame:\r
4807       CopyGameToClipboard();\r
4808       break;\r
4809 \r
4810     case IDM_PasteGame:\r
4811       PasteGameFromClipboard();\r
4812       break;\r
4813 \r
4814     case IDM_CopyGameListToClipboard:\r
4815       CopyGameListToClipboard();\r
4816       break;\r
4817 \r
4818     /* [AS] Autodetect FEN or PGN data */\r
4819     case IDM_PasteAny:\r
4820       PasteGameOrFENFromClipboard();\r
4821       break;\r
4822 \r
4823     /* [AS] Move history */\r
4824     case IDM_ShowMoveHistory:\r
4825         if( MoveHistoryIsUp() ) {\r
4826             MoveHistoryPopDown();\r
4827         }\r
4828         else {\r
4829             MoveHistoryPopUp();\r
4830         }\r
4831         break;\r
4832 \r
4833     /* [AS] Eval graph */\r
4834     case IDM_ShowEvalGraph:\r
4835         if( EvalGraphIsUp() ) {\r
4836             EvalGraphPopDown();\r
4837         }\r
4838         else {\r
4839             EvalGraphPopUp();\r
4840             SetFocus(hwndMain);\r
4841         }\r
4842         break;\r
4843 \r
4844     /* [AS] Engine output */\r
4845     case IDM_ShowEngineOutput:\r
4846         if( EngineOutputIsUp() ) {\r
4847             EngineOutputPopDown();\r
4848         }\r
4849         else {\r
4850             EngineOutputPopUp();\r
4851         }\r
4852         break;\r
4853 \r
4854     /* [AS] User adjudication */\r
4855     case IDM_UserAdjudication_White:\r
4856         UserAdjudicationEvent( +1 );\r
4857         break;\r
4858 \r
4859     case IDM_UserAdjudication_Black:\r
4860         UserAdjudicationEvent( -1 );\r
4861         break;\r
4862 \r
4863     case IDM_UserAdjudication_Draw:\r
4864         UserAdjudicationEvent( 0 );\r
4865         break;\r
4866 \r
4867     /* [AS] Game list options dialog */\r
4868     case IDM_GameListOptions:\r
4869       GameListOptions();\r
4870       break;\r
4871 \r
4872     case IDM_NewChat:\r
4873       ChatPopUp(NULL);\r
4874       break;\r
4875 \r
4876     case IDM_CopyPosition:\r
4877       CopyFENToClipboard();\r
4878       break;\r
4879 \r
4880     case IDM_PastePosition:\r
4881       PasteFENFromClipboard();\r
4882       break;\r
4883 \r
4884     case IDM_MailMove:\r
4885       MailMoveEvent();\r
4886       break;\r
4887 \r
4888     case IDM_ReloadCMailMsg:\r
4889       Reset(TRUE, TRUE);\r
4890       ReloadCmailMsgEvent(FALSE);\r
4891       break;\r
4892 \r
4893     case IDM_Minimize:\r
4894       ShowWindow(hwnd, SW_MINIMIZE);\r
4895       break;\r
4896 \r
4897     case IDM_Exit:\r
4898       ExitEvent(0);\r
4899       break;\r
4900 \r
4901     case IDM_MachineWhite:\r
4902       MachineWhiteEvent();\r
4903       /*\r
4904        * refresh the tags dialog only if it's visible\r
4905        */\r
4906       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4907           char *tags;\r
4908           tags = PGNTags(&gameInfo);\r
4909           TagsPopUp(tags, CmailMsg());\r
4910           free(tags);\r
4911       }\r
4912       SAY("computer starts playing white");\r
4913       break;\r
4914 \r
4915     case IDM_MachineBlack:\r
4916       MachineBlackEvent();\r
4917       /*\r
4918        * refresh the tags dialog only if it's visible\r
4919        */\r
4920       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4921           char *tags;\r
4922           tags = PGNTags(&gameInfo);\r
4923           TagsPopUp(tags, CmailMsg());\r
4924           free(tags);\r
4925       }\r
4926       SAY("computer starts playing black");\r
4927       break;\r
4928 \r
4929     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4930       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4931       break;\r
4932 \r
4933     case IDM_TwoMachines:\r
4934       TwoMachinesEvent();\r
4935       /*\r
4936        * refresh the tags dialog only if it's visible\r
4937        */\r
4938       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4939           char *tags;\r
4940           tags = PGNTags(&gameInfo);\r
4941           TagsPopUp(tags, CmailMsg());\r
4942           free(tags);\r
4943       }\r
4944       SAY("computer starts playing both sides");\r
4945       break;\r
4946 \r
4947     case IDM_AnalysisMode:\r
4948       if(AnalyzeModeEvent()) {\r
4949         SAY("analyzing current position");\r
4950       }\r
4951       break;\r
4952 \r
4953     case IDM_AnalyzeFile:\r
4954       AnalyzeFileEvent();\r
4955       break;\r
4956 \r
4957     case IDM_IcsClient:\r
4958       IcsClientEvent();\r
4959       break;\r
4960 \r
4961     case IDM_EditGame:\r
4962     case IDM_EditGame2:\r
4963       EditGameEvent();\r
4964       SAY("edit game");\r
4965       break;\r
4966 \r
4967     case IDM_EditPosition:\r
4968     case IDM_EditPosition2:\r
4969       EditPositionEvent();\r
4970       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4971       break;\r
4972 \r
4973     case IDM_Training:\r
4974       TrainingEvent();\r
4975       break;\r
4976 \r
4977     case IDM_ShowGameList:\r
4978       ShowGameListProc();\r
4979       break;\r
4980 \r
4981     case IDM_EditProgs1:\r
4982       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4983       break;\r
4984 \r
4985     case IDM_LoadProg1:\r
4986      LoadEnginePopUp(hwndMain, 0);\r
4987       break;\r
4988 \r
4989     case IDM_LoadProg2:\r
4990      LoadEnginePopUp(hwndMain, 1);\r
4991       break;\r
4992 \r
4993     case IDM_EditServers:\r
4994       EditTagsPopUp(icsNames, &icsNames);\r
4995       break;\r
4996 \r
4997     case IDM_EditTags:\r
4998     case IDM_Tags:\r
4999       EditTagsProc();\r
5000       break;\r
5001 \r
5002     case IDM_EditBook:\r
5003       EditBookEvent();\r
5004       break;\r
5005 \r
5006     case IDM_EditComment:\r
5007     case IDM_Comment:\r
5008       if (commentUp && editComment) {\r
5009         CommentPopDown();\r
5010       } else {\r
5011         EditCommentEvent();\r
5012       }\r
5013       break;\r
5014 \r
5015     case IDM_Pause:\r
5016       PauseEvent();\r
5017       break;\r
5018 \r
5019     case IDM_Accept:\r
5020       AcceptEvent();\r
5021       break;\r
5022 \r
5023     case IDM_Decline:\r
5024       DeclineEvent();\r
5025       break;\r
5026 \r
5027     case IDM_Rematch:\r
5028       RematchEvent();\r
5029       break;\r
5030 \r
5031     case IDM_CallFlag:\r
5032       CallFlagEvent();\r
5033       break;\r
5034 \r
5035     case IDM_Draw:\r
5036       DrawEvent();\r
5037       break;\r
5038 \r
5039     case IDM_Adjourn:\r
5040       AdjournEvent();\r
5041       break;\r
5042 \r
5043     case IDM_Abort:\r
5044       AbortEvent();\r
5045       break;\r
5046 \r
5047     case IDM_Resign:\r
5048       ResignEvent();\r
5049       break;\r
5050 \r
5051     case IDM_StopObserving:\r
5052       StopObservingEvent();\r
5053       break;\r
5054 \r
5055     case IDM_StopExamining:\r
5056       StopExaminingEvent();\r
5057       break;\r
5058 \r
5059     case IDM_Upload:\r
5060       UploadGameEvent();\r
5061       break;\r
5062 \r
5063     case IDM_TypeInMove:\r
5064       TypeInEvent('\000');\r
5065       break;\r
5066 \r
5067     case IDM_TypeInName:\r
5068       PopUpNameDialog('\000');\r
5069       break;\r
5070 \r
5071     case IDM_Backward:\r
5072       BackwardEvent();\r
5073       SetFocus(hwndMain);\r
5074       break;\r
5075 \r
5076     JAWS_MENU_ITEMS\r
5077 \r
5078     case IDM_Forward:\r
5079       ForwardEvent();\r
5080       SetFocus(hwndMain);\r
5081       break;\r
5082 \r
5083     case IDM_ToStart:\r
5084       ToStartEvent();\r
5085       SetFocus(hwndMain);\r
5086       break;\r
5087 \r
5088     case IDM_ToEnd:\r
5089       ToEndEvent();\r
5090       SetFocus(hwndMain);\r
5091       break;\r
5092 \r
5093     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5094     case OPT_GameListPrev:\r
5095       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5096       break;\r
5097 \r
5098     case IDM_Revert:\r
5099       RevertEvent(FALSE);\r
5100       break;\r
5101 \r
5102     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5103       RevertEvent(TRUE);\r
5104       break;\r
5105 \r
5106     case IDM_TruncateGame:\r
5107       TruncateGameEvent();\r
5108       break;\r
5109 \r
5110     case IDM_MoveNow:\r
5111       MoveNowEvent();\r
5112       break;\r
5113 \r
5114     case IDM_RetractMove:\r
5115       RetractMoveEvent();\r
5116       break;\r
5117 \r
5118     case IDM_FlipView:\r
5119       flipView = !flipView;\r
5120       DrawPosition(FALSE, NULL);\r
5121       break;\r
5122 \r
5123     case IDM_FlipClock:\r
5124       flipClock = !flipClock;\r
5125       DisplayBothClocks();\r
5126       DisplayLogos();\r
5127       break;\r
5128 \r
5129     case IDM_MuteSounds:\r
5130       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5131       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5132                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5133       break;\r
5134 \r
5135     case IDM_GeneralOptions:\r
5136       GeneralOptionsPopup(hwnd);\r
5137       DrawPosition(TRUE, NULL);\r
5138       break;\r
5139 \r
5140     case IDM_BoardOptions:\r
5141       BoardOptionsPopup(hwnd);\r
5142       break;\r
5143 \r
5144     case IDM_EnginePlayOptions:\r
5145       EnginePlayOptionsPopup(hwnd);\r
5146       break;\r
5147 \r
5148     case IDM_Engine1Options:\r
5149       EngineOptionsPopup(hwnd, &first);\r
5150       break;\r
5151 \r
5152     case IDM_Engine2Options:\r
5153       savedHwnd = hwnd;\r
5154       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5155       EngineOptionsPopup(hwnd, &second);\r
5156       break;\r
5157 \r
5158     case IDM_OptionsUCI:\r
5159       UciOptionsPopup(hwnd);\r
5160       break;\r
5161 \r
5162     case IDM_Tourney:\r
5163       TourneyPopup(hwnd);\r
5164       break;\r
5165 \r
5166     case IDM_IcsOptions:\r
5167       IcsOptionsPopup(hwnd);\r
5168       break;\r
5169 \r
5170     case IDM_Fonts:\r
5171       FontsOptionsPopup(hwnd);\r
5172       break;\r
5173 \r
5174     case IDM_Sounds:\r
5175       SoundOptionsPopup(hwnd);\r
5176       break;\r
5177 \r
5178     case IDM_CommPort:\r
5179       CommPortOptionsPopup(hwnd);\r
5180       break;\r
5181 \r
5182     case IDM_LoadOptions:\r
5183       LoadOptionsPopup(hwnd);\r
5184       break;\r
5185 \r
5186     case IDM_SaveOptions:\r
5187       SaveOptionsPopup(hwnd);\r
5188       break;\r
5189 \r
5190     case IDM_TimeControl:\r
5191       TimeControlOptionsPopup(hwnd);\r
5192       break;\r
5193 \r
5194     case IDM_SaveSettings:\r
5195       SaveSettings(settingsFileName);\r
5196       break;\r
5197 \r
5198     case IDM_SaveSettingsOnExit:\r
5199       saveSettingsOnExit = !saveSettingsOnExit;\r
5200       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5201                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5202                                          MF_CHECKED : MF_UNCHECKED));\r
5203       break;\r
5204 \r
5205     case IDM_Hint:\r
5206       HintEvent();\r
5207       break;\r
5208 \r
5209     case IDM_Book:\r
5210       BookEvent();\r
5211       break;\r
5212 \r
5213     case IDM_AboutGame:\r
5214       AboutGameEvent();\r
5215       break;\r
5216 \r
5217     case IDM_Debug:\r
5218       appData.debugMode = !appData.debugMode;\r
5219       if (appData.debugMode) {\r
5220         char dir[MSG_SIZ];\r
5221         GetCurrentDirectory(MSG_SIZ, dir);\r
5222         SetCurrentDirectory(installDir);\r
5223         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5224         SetCurrentDirectory(dir);\r
5225         setbuf(debugFP, NULL);\r
5226       } else {\r
5227         fclose(debugFP);\r
5228         debugFP = NULL;\r
5229       }\r
5230       break;\r
5231 \r
5232     case IDM_HELPCONTENTS:\r
5233       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5234           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5235           MessageBox (GetFocus(),\r
5236                     _("Unable to activate help"),\r
5237                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5238       }\r
5239       break;\r
5240 \r
5241     case IDM_HELPSEARCH:\r
5242         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5243             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5244         MessageBox (GetFocus(),\r
5245                     _("Unable to activate help"),\r
5246                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5247       }\r
5248       break;\r
5249 \r
5250     case IDM_HELPHELP:\r
5251       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5252         MessageBox (GetFocus(),\r
5253                     _("Unable to activate help"),\r
5254                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5255       }\r
5256       break;\r
5257 \r
5258     case IDM_ABOUT:\r
5259       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5260       DialogBox(hInst, \r
5261         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5262         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5263       FreeProcInstance(lpProc);\r
5264       break;\r
5265 \r
5266     case IDM_DirectCommand1:\r
5267       AskQuestionEvent(_("Direct Command"),\r
5268                        _("Send to chess program:"), "", "1");\r
5269       break;\r
5270     case IDM_DirectCommand2:\r
5271       AskQuestionEvent(_("Direct Command"),\r
5272                        _("Send to second chess program:"), "", "2");\r
5273       break;\r
5274 \r
5275     case EP_WhitePawn:\r
5276       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5277       fromX = fromY = -1;\r
5278       break;\r
5279 \r
5280     case EP_WhiteKnight:\r
5281       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5282       fromX = fromY = -1;\r
5283       break;\r
5284 \r
5285     case EP_WhiteBishop:\r
5286       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5287       fromX = fromY = -1;\r
5288       break;\r
5289 \r
5290     case EP_WhiteRook:\r
5291       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_WhiteQueen:\r
5296       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_WhiteFerz:\r
5301       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_WhiteWazir:\r
5306       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_WhiteAlfil:\r
5311       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_WhiteCannon:\r
5316       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case EP_WhiteCardinal:\r
5321       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case EP_WhiteMarshall:\r
5326       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case EP_WhiteKing:\r
5331       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case EP_BlackPawn:\r
5336       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case EP_BlackKnight:\r
5341       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case EP_BlackBishop:\r
5346       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5347       fromX = fromY = -1;\r
5348       break;\r
5349 \r
5350     case EP_BlackRook:\r
5351       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5352       fromX = fromY = -1;\r
5353       break;\r
5354 \r
5355     case EP_BlackQueen:\r
5356       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case EP_BlackFerz:\r
5361       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case EP_BlackWazir:\r
5366       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case EP_BlackAlfil:\r
5371       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case EP_BlackCannon:\r
5376       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case EP_BlackCardinal:\r
5381       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case EP_BlackMarshall:\r
5386       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case EP_BlackKing:\r
5391       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5392       fromX = fromY = -1;\r
5393       break;\r
5394 \r
5395     case EP_EmptySquare:\r
5396       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case EP_ClearBoard:\r
5401       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5402       fromX = fromY = -1;\r
5403       break;\r
5404 \r
5405     case EP_White:\r
5406       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5407       fromX = fromY = -1;\r
5408       break;\r
5409 \r
5410     case EP_Black:\r
5411       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5412       fromX = fromY = -1;\r
5413       break;\r
5414 \r
5415     case EP_Promote:\r
5416       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_Demote:\r
5421       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case DP_Pawn:\r
5426       DropMenuEvent(WhitePawn, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case DP_Knight:\r
5431       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case DP_Bishop:\r
5436       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case DP_Rook:\r
5441       DropMenuEvent(WhiteRook, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case DP_Queen:\r
5446       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case IDM_English:\r
5451       barbaric = 0; appData.language = "";\r
5452       TranslateMenus(0);\r
5453       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5454       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5455       lastChecked = wmId;\r
5456       break;\r
5457 \r
5458     default:\r
5459       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5460           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5461       else\r
5462       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5463           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5464           TranslateMenus(0);\r
5465           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5466           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5467           lastChecked = wmId;\r
5468           break;\r
5469       }\r
5470       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5471     }\r
5472     break;\r
5473 \r
5474   case WM_TIMER:\r
5475     switch (wParam) {\r
5476     case CLOCK_TIMER_ID:\r
5477       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5478       clockTimerEvent = 0;\r
5479       DecrementClocks(); /* call into back end */\r
5480       break;\r
5481     case LOAD_GAME_TIMER_ID:\r
5482       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5483       loadGameTimerEvent = 0;\r
5484       AutoPlayGameLoop(); /* call into back end */\r
5485       break;\r
5486     case ANALYSIS_TIMER_ID:\r
5487       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5488                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5489         AnalysisPeriodicEvent(0);\r
5490       } else {\r
5491         KillTimer(hwnd, analysisTimerEvent);\r
5492         analysisTimerEvent = 0;\r
5493       }\r
5494       break;\r
5495     case DELAYED_TIMER_ID:\r
5496       KillTimer(hwnd, delayedTimerEvent);\r
5497       delayedTimerEvent = 0;\r
5498       delayedTimerCallback();\r
5499       break;\r
5500     }\r
5501     break;\r
5502 \r
5503   case WM_USER_Input:\r
5504     InputEvent(hwnd, message, wParam, lParam);\r
5505     break;\r
5506 \r
5507   /* [AS] Also move "attached" child windows */\r
5508   case WM_WINDOWPOSCHANGING:\r
5509 \r
5510     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5511         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5512 \r
5513         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5514             /* Window is moving */\r
5515             RECT rcMain;\r
5516 \r
5517 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5518             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5519             rcMain.right  = wpMain.x + wpMain.width;\r
5520             rcMain.top    = wpMain.y;\r
5521             rcMain.bottom = wpMain.y + wpMain.height;\r
5522             \r
5523             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5524             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5525             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5526             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5527             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5528             wpMain.x = lpwp->x;\r
5529             wpMain.y = lpwp->y;\r
5530         }\r
5531     }\r
5532     break;\r
5533 \r
5534   /* [AS] Snapping */\r
5535   case WM_ENTERSIZEMOVE:\r
5536     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5537     if (hwnd == hwndMain) {\r
5538       doingSizing = TRUE;\r
5539       lastSizing = 0;\r
5540     }\r
5541     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5542     break;\r
5543 \r
5544   case WM_SIZING:\r
5545     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5546     if (hwnd == hwndMain) {\r
5547       lastSizing = wParam;\r
5548     }\r
5549     break;\r
5550 \r
5551   case WM_MOVING:\r
5552     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5553       return OnMoving( &sd, hwnd, wParam, lParam );\r
5554 \r
5555   case WM_EXITSIZEMOVE:\r
5556     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5557     if (hwnd == hwndMain) {\r
5558       RECT client;\r
5559       doingSizing = FALSE;\r
5560       InvalidateRect(hwnd, &boardRect, FALSE);\r
5561       GetClientRect(hwnd, &client);\r
5562       ResizeBoard(client.right, client.bottom, lastSizing);\r
5563       lastSizing = 0;\r
5564       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5565     }\r
5566     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5567     break;\r
5568 \r
5569   case WM_DESTROY: /* message: window being destroyed */\r
5570     PostQuitMessage(0);\r
5571     break;\r
5572 \r
5573   case WM_CLOSE:\r
5574     if (hwnd == hwndMain) {\r
5575       ExitEvent(0);\r
5576     }\r
5577     break;\r
5578 \r
5579   default:      /* Passes it on if unprocessed */\r
5580     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5581   }\r
5582   return 0;\r
5583 }\r
5584 \r
5585 /*---------------------------------------------------------------------------*\\r
5586  *\r
5587  * Misc utility routines\r
5588  *\r
5589 \*---------------------------------------------------------------------------*/\r
5590 \r
5591 /*\r
5592  * Decent random number generator, at least not as bad as Windows\r
5593  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5594  */\r
5595 unsigned int randstate;\r
5596 \r
5597 int\r
5598 myrandom(void)\r
5599 {\r
5600   randstate = randstate * 1664525 + 1013904223;\r
5601   return (int) randstate & 0x7fffffff;\r
5602 }\r
5603 \r
5604 void\r
5605 mysrandom(unsigned int seed)\r
5606 {\r
5607   randstate = seed;\r
5608 }\r
5609 \r
5610 \r
5611 /* \r
5612  * returns TRUE if user selects a different color, FALSE otherwise \r
5613  */\r
5614 \r
5615 BOOL\r
5616 ChangeColor(HWND hwnd, COLORREF *which)\r
5617 {\r
5618   static BOOL firstTime = TRUE;\r
5619   static DWORD customColors[16];\r
5620   CHOOSECOLOR cc;\r
5621   COLORREF newcolor;\r
5622   int i;\r
5623   ColorClass ccl;\r
5624 \r
5625   if (firstTime) {\r
5626     /* Make initial colors in use available as custom colors */\r
5627     /* Should we put the compiled-in defaults here instead? */\r
5628     i = 0;\r
5629     customColors[i++] = lightSquareColor & 0xffffff;\r
5630     customColors[i++] = darkSquareColor & 0xffffff;\r
5631     customColors[i++] = whitePieceColor & 0xffffff;\r
5632     customColors[i++] = blackPieceColor & 0xffffff;\r
5633     customColors[i++] = highlightSquareColor & 0xffffff;\r
5634     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5635 \r
5636     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5637       customColors[i++] = textAttribs[ccl].color;\r
5638     }\r
5639     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5640     firstTime = FALSE;\r
5641   }\r
5642 \r
5643   cc.lStructSize = sizeof(cc);\r
5644   cc.hwndOwner = hwnd;\r
5645   cc.hInstance = NULL;\r
5646   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5647   cc.lpCustColors = (LPDWORD) customColors;\r
5648   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5649 \r
5650   if (!ChooseColor(&cc)) return FALSE;\r
5651 \r
5652   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5653   if (newcolor == *which) return FALSE;\r
5654   *which = newcolor;\r
5655   return TRUE;\r
5656 \r
5657   /*\r
5658   InitDrawingColors();\r
5659   InvalidateRect(hwnd, &boardRect, FALSE);\r
5660   */\r
5661 }\r
5662 \r
5663 BOOLEAN\r
5664 MyLoadSound(MySound *ms)\r
5665 {\r
5666   BOOL ok = FALSE;\r
5667   struct stat st;\r
5668   FILE *f;\r
5669 \r
5670   if (ms->data && ms->flag) free(ms->data);\r
5671   ms->data = NULL;\r
5672 \r
5673   switch (ms->name[0]) {\r
5674   case NULLCHAR:\r
5675     /* Silence */\r
5676     ok = TRUE;\r
5677     break;\r
5678   case '$':\r
5679     /* System sound from Control Panel.  Don't preload here. */\r
5680     ok = TRUE;\r
5681     break;\r
5682   case '!':\r
5683     if (ms->name[1] == NULLCHAR) {\r
5684       /* "!" alone = silence */\r
5685       ok = TRUE;\r
5686     } else {\r
5687       /* Builtin wave resource.  Error if not found. */\r
5688       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5689       if (h == NULL) break;\r
5690       ms->data = (void *)LoadResource(hInst, h);\r
5691       ms->flag = 0; // not maloced, so cannot be freed!\r
5692       if (h == NULL) break;\r
5693       ok = TRUE;\r
5694     }\r
5695     break;\r
5696   default:\r
5697     /* .wav file.  Error if not found. */\r
5698     f = fopen(ms->name, "rb");\r
5699     if (f == NULL) break;\r
5700     if (fstat(fileno(f), &st) < 0) break;\r
5701     ms->data = malloc(st.st_size);\r
5702     ms->flag = 1;\r
5703     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5704     fclose(f);\r
5705     ok = TRUE;\r
5706     break;\r
5707   }\r
5708   if (!ok) {\r
5709     char buf[MSG_SIZ];\r
5710       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5711     DisplayError(buf, GetLastError());\r
5712   }\r
5713   return ok;\r
5714 }\r
5715 \r
5716 BOOLEAN\r
5717 MyPlaySound(MySound *ms)\r
5718 {\r
5719   BOOLEAN ok = FALSE;\r
5720 \r
5721   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5722   switch (ms->name[0]) {\r
5723   case NULLCHAR:\r
5724         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5725     /* Silence */\r
5726     ok = TRUE;\r
5727     break;\r
5728   case '$':\r
5729     /* System sound from Control Panel (deprecated feature).\r
5730        "$" alone or an unset sound name gets default beep (still in use). */\r
5731     if (ms->name[1]) {\r
5732       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5733     }\r
5734     if (!ok) ok = MessageBeep(MB_OK);\r
5735     break; \r
5736   case '!':\r
5737     /* Builtin wave resource, or "!" alone for silence */\r
5738     if (ms->name[1]) {\r
5739       if (ms->data == NULL) return FALSE;\r
5740       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5741     } else {\r
5742       ok = TRUE;\r
5743     }\r
5744     break;\r
5745   default:\r
5746     /* .wav file.  Error if not found. */\r
5747     if (ms->data == NULL) return FALSE;\r
5748     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5749     break;\r
5750   }\r
5751   /* Don't print an error: this can happen innocently if the sound driver\r
5752      is busy; for instance, if another instance of WinBoard is playing\r
5753      a sound at about the same time. */\r
5754   return ok;\r
5755 }\r
5756 \r
5757 \r
5758 LRESULT CALLBACK\r
5759 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5760 {\r
5761   BOOL ok;\r
5762   OPENFILENAME *ofn;\r
5763   static UINT *number; /* gross that this is static */\r
5764 \r
5765   switch (message) {\r
5766   case WM_INITDIALOG: /* message: initialize dialog box */\r
5767     /* Center the dialog over the application window */\r
5768     ofn = (OPENFILENAME *) lParam;\r
5769     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5770       number = (UINT *) ofn->lCustData;\r
5771       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5772     } else {\r
5773       number = NULL;\r
5774     }\r
5775     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5776     Translate(hDlg, 1536);\r
5777     return FALSE;  /* Allow for further processing */\r
5778 \r
5779   case WM_COMMAND:\r
5780     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5781       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5782     }\r
5783     return FALSE;  /* Allow for further processing */\r
5784   }\r
5785   return FALSE;\r
5786 }\r
5787 \r
5788 UINT APIENTRY\r
5789 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5790 {\r
5791   static UINT *number;\r
5792   OPENFILENAME *ofname;\r
5793   OFNOTIFY *ofnot;\r
5794   switch (uiMsg) {\r
5795   case WM_INITDIALOG:\r
5796     Translate(hdlg, DLG_IndexNumber);\r
5797     ofname = (OPENFILENAME *)lParam;\r
5798     number = (UINT *)(ofname->lCustData);\r
5799     break;\r
5800   case WM_NOTIFY:\r
5801     ofnot = (OFNOTIFY *)lParam;\r
5802     if (ofnot->hdr.code == CDN_FILEOK) {\r
5803       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5804     }\r
5805     break;\r
5806   }\r
5807   return 0;\r
5808 }\r
5809 \r
5810 \r
5811 FILE *\r
5812 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5813                char *nameFilt, char *dlgTitle, UINT *number,\r
5814                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5815 {\r
5816   OPENFILENAME openFileName;\r
5817   char buf1[MSG_SIZ];\r
5818   FILE *f;\r
5819 \r
5820   if (fileName == NULL) fileName = buf1;\r
5821   if (defName == NULL) {\r
5822     safeStrCpy(fileName, "*.", 3 );\r
5823     strcat(fileName, defExt);\r
5824   } else {\r
5825     safeStrCpy(fileName, defName, MSG_SIZ );\r
5826   }\r
5827     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5828   if (number) *number = 0;\r
5829 \r
5830   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5831   openFileName.hwndOwner         = hwnd;\r
5832   openFileName.hInstance         = (HANDLE) hInst;\r
5833   openFileName.lpstrFilter       = nameFilt;\r
5834   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5835   openFileName.nMaxCustFilter    = 0L;\r
5836   openFileName.nFilterIndex      = 1L;\r
5837   openFileName.lpstrFile         = fileName;\r
5838   openFileName.nMaxFile          = MSG_SIZ;\r
5839   openFileName.lpstrFileTitle    = fileTitle;\r
5840   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5841   openFileName.lpstrInitialDir   = NULL;\r
5842   openFileName.lpstrTitle        = dlgTitle;\r
5843   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5844     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5845     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5846     | (oldDialog ? 0 : OFN_EXPLORER);\r
5847   openFileName.nFileOffset       = 0;\r
5848   openFileName.nFileExtension    = 0;\r
5849   openFileName.lpstrDefExt       = defExt;\r
5850   openFileName.lCustData         = (LONG) number;\r
5851   openFileName.lpfnHook          = oldDialog ?\r
5852     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5853   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5854 \r
5855   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5856                         GetOpenFileName(&openFileName)) {\r
5857     /* open the file */\r
5858     f = fopen(openFileName.lpstrFile, write);\r
5859     if (f == NULL) {\r
5860       MessageBox(hwnd, _("File open failed"), NULL,\r
5861                  MB_OK|MB_ICONEXCLAMATION);\r
5862       return NULL;\r
5863     }\r
5864   } else {\r
5865     int err = CommDlgExtendedError();\r
5866     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5867     return FALSE;\r
5868   }\r
5869   return f;\r
5870 }\r
5871 \r
5872 \r
5873 \r
5874 VOID APIENTRY\r
5875 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5876 {\r
5877   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5878 \r
5879   /*\r
5880    * Get the first pop-up menu in the menu template. This is the\r
5881    * menu that TrackPopupMenu displays.\r
5882    */\r
5883   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5884   TranslateOneMenu(10, hmenuTrackPopup);\r
5885 \r
5886   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5887 \r
5888   /*\r
5889    * TrackPopup uses screen coordinates, so convert the\r
5890    * coordinates of the mouse click to screen coordinates.\r
5891    */\r
5892   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5893 \r
5894   /* Draw and track the floating pop-up menu. */\r
5895   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5896                  pt.x, pt.y, 0, hwnd, NULL);\r
5897 \r
5898   /* Destroy the menu.*/\r
5899   DestroyMenu(hmenu);\r
5900 }\r
5901    \r
5902 typedef struct {\r
5903   HWND hDlg, hText;\r
5904   int sizeX, sizeY, newSizeX, newSizeY;\r
5905   HDWP hdwp;\r
5906 } ResizeEditPlusButtonsClosure;\r
5907 \r
5908 BOOL CALLBACK\r
5909 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5910 {\r
5911   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5912   RECT rect;\r
5913   POINT pt;\r
5914 \r
5915   if (hChild == cl->hText) return TRUE;\r
5916   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5917   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5918   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5919   ScreenToClient(cl->hDlg, &pt);\r
5920   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5921     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5922   return TRUE;\r
5923 }\r
5924 \r
5925 /* Resize a dialog that has a (rich) edit field filling most of\r
5926    the top, with a row of buttons below */\r
5927 VOID\r
5928 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5929 {\r
5930   RECT rectText;\r
5931   int newTextHeight, newTextWidth;\r
5932   ResizeEditPlusButtonsClosure cl;\r
5933   \r
5934   /*if (IsIconic(hDlg)) return;*/\r
5935   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5936   \r
5937   cl.hdwp = BeginDeferWindowPos(8);\r
5938 \r
5939   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5940   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5941   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5942   if (newTextHeight < 0) {\r
5943     newSizeY += -newTextHeight;\r
5944     newTextHeight = 0;\r
5945   }\r
5946   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5947     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5948 \r
5949   cl.hDlg = hDlg;\r
5950   cl.hText = hText;\r
5951   cl.sizeX = sizeX;\r
5952   cl.sizeY = sizeY;\r
5953   cl.newSizeX = newSizeX;\r
5954   cl.newSizeY = newSizeY;\r
5955   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5956 \r
5957   EndDeferWindowPos(cl.hdwp);\r
5958 }\r
5959 \r
5960 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5961 {\r
5962     RECT    rChild, rParent;\r
5963     int     wChild, hChild, wParent, hParent;\r
5964     int     wScreen, hScreen, xNew, yNew;\r
5965     HDC     hdc;\r
5966 \r
5967     /* Get the Height and Width of the child window */\r
5968     GetWindowRect (hwndChild, &rChild);\r
5969     wChild = rChild.right - rChild.left;\r
5970     hChild = rChild.bottom - rChild.top;\r
5971 \r
5972     /* Get the Height and Width of the parent window */\r
5973     GetWindowRect (hwndParent, &rParent);\r
5974     wParent = rParent.right - rParent.left;\r
5975     hParent = rParent.bottom - rParent.top;\r
5976 \r
5977     /* Get the display limits */\r
5978     hdc = GetDC (hwndChild);\r
5979     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5980     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5981     ReleaseDC(hwndChild, hdc);\r
5982 \r
5983     /* Calculate new X position, then adjust for screen */\r
5984     xNew = rParent.left + ((wParent - wChild) /2);\r
5985     if (xNew < 0) {\r
5986         xNew = 0;\r
5987     } else if ((xNew+wChild) > wScreen) {\r
5988         xNew = wScreen - wChild;\r
5989     }\r
5990 \r
5991     /* Calculate new Y position, then adjust for screen */\r
5992     if( mode == 0 ) {\r
5993         yNew = rParent.top  + ((hParent - hChild) /2);\r
5994     }\r
5995     else {\r
5996         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5997     }\r
5998 \r
5999     if (yNew < 0) {\r
6000         yNew = 0;\r
6001     } else if ((yNew+hChild) > hScreen) {\r
6002         yNew = hScreen - hChild;\r
6003     }\r
6004 \r
6005     /* Set it, and return */\r
6006     return SetWindowPos (hwndChild, NULL,\r
6007                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6008 }\r
6009 \r
6010 /* Center one window over another */\r
6011 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6012 {\r
6013     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6014 }\r
6015 \r
6016 /*---------------------------------------------------------------------------*\\r
6017  *\r
6018  * Startup Dialog functions\r
6019  *\r
6020 \*---------------------------------------------------------------------------*/\r
6021 void\r
6022 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6023 {\r
6024   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6025 \r
6026   while (*cd != NULL) {\r
6027     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6028     cd++;\r
6029   }\r
6030 }\r
6031 \r
6032 void\r
6033 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6034 {\r
6035   char buf1[MAX_ARG_LEN];\r
6036   int len;\r
6037 \r
6038   if (str[0] == '@') {\r
6039     FILE* f = fopen(str + 1, "r");\r
6040     if (f == NULL) {\r
6041       DisplayFatalError(str + 1, errno, 2);\r
6042       return;\r
6043     }\r
6044     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6045     fclose(f);\r
6046     buf1[len] = NULLCHAR;\r
6047     str = buf1;\r
6048   }\r
6049 \r
6050   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6051 \r
6052   for (;;) {\r
6053     char buf[MSG_SIZ];\r
6054     char *end = strchr(str, '\n');\r
6055     if (end == NULL) return;\r
6056     memcpy(buf, str, end - str);\r
6057     buf[end - str] = NULLCHAR;\r
6058     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6059     str = end + 1;\r
6060   }\r
6061 }\r
6062 \r
6063 void\r
6064 SetStartupDialogEnables(HWND hDlg)\r
6065 {\r
6066   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6067     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6068     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6069   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6070     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6071   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6072     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6073   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6074     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6075   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6076     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6077     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6078     IsDlgButtonChecked(hDlg, OPT_View));\r
6079 }\r
6080 \r
6081 char *\r
6082 QuoteForFilename(char *filename)\r
6083 {\r
6084   int dquote, space;\r
6085   dquote = strchr(filename, '"') != NULL;\r
6086   space = strchr(filename, ' ') != NULL;\r
6087   if (dquote || space) {\r
6088     if (dquote) {\r
6089       return "'";\r
6090     } else {\r
6091       return "\"";\r
6092     }\r
6093   } else {\r
6094     return "";\r
6095   }\r
6096 }\r
6097 \r
6098 VOID\r
6099 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6100 {\r
6101   char buf[MSG_SIZ];\r
6102   char *q;\r
6103 \r
6104   InitComboStringsFromOption(hwndCombo, nthnames);\r
6105   q = QuoteForFilename(nthcp);\r
6106     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6107   if (*nthdir != NULLCHAR) {\r
6108     q = QuoteForFilename(nthdir);\r
6109       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6110   }\r
6111   if (*nthcp == NULLCHAR) {\r
6112     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6113   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6114     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6115     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6116   }\r
6117 }\r
6118 \r
6119 LRESULT CALLBACK\r
6120 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6121 {\r
6122   char buf[MSG_SIZ];\r
6123   HANDLE hwndCombo;\r
6124   char *p;\r
6125 \r
6126   switch (message) {\r
6127   case WM_INITDIALOG:\r
6128     /* Center the dialog */\r
6129     CenterWindow (hDlg, GetDesktopWindow());\r
6130     Translate(hDlg, DLG_Startup);\r
6131     /* Initialize the dialog items */\r
6132     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6133                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6134                   firstChessProgramNames);\r
6135     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6136                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6137                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6138     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6139     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6140       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6141     if (*appData.icsHelper != NULLCHAR) {\r
6142       char *q = QuoteForFilename(appData.icsHelper);\r
6143       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6144     }\r
6145     if (*appData.icsHost == NULLCHAR) {\r
6146       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6147       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6148     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6149       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6150       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6151     }\r
6152 \r
6153     if (appData.icsActive) {\r
6154       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6155     }\r
6156     else if (appData.noChessProgram) {\r
6157       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6158     }\r
6159     else {\r
6160       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6161     }\r
6162 \r
6163     SetStartupDialogEnables(hDlg);\r
6164     return TRUE;\r
6165 \r
6166   case WM_COMMAND:\r
6167     switch (LOWORD(wParam)) {\r
6168     case IDOK:\r
6169       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6170         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6171         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6172         p = buf;\r
6173         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6174         ParseArgs(StringGet, &p);\r
6175         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6176         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6177         p = buf;\r
6178         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6179         ParseArgs(StringGet, &p);\r
6180         SwapEngines(singleList); // ... and then make it 'second'\r
6181         appData.noChessProgram = FALSE;\r
6182         appData.icsActive = FALSE;\r
6183       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6184         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6185         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6186         p = buf;\r
6187         ParseArgs(StringGet, &p);\r
6188         if (appData.zippyPlay) {\r
6189           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6190           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6191           p = buf;\r
6192           ParseArgs(StringGet, &p);\r
6193         }\r
6194       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6195         appData.noChessProgram = TRUE;\r
6196         appData.icsActive = FALSE;\r
6197       } else {\r
6198         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6199                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6200         return TRUE;\r
6201       }\r
6202       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6203         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6204         p = buf;\r
6205         ParseArgs(StringGet, &p);\r
6206       }\r
6207       EndDialog(hDlg, TRUE);\r
6208       return TRUE;\r
6209 \r
6210     case IDCANCEL:\r
6211       ExitEvent(0);\r
6212       return TRUE;\r
6213 \r
6214     case IDM_HELPCONTENTS:\r
6215       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6216         MessageBox (GetFocus(),\r
6217                     _("Unable to activate help"),\r
6218                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6219       }\r
6220       break;\r
6221 \r
6222     default:\r
6223       SetStartupDialogEnables(hDlg);\r
6224       break;\r
6225     }\r
6226     break;\r
6227   }\r
6228   return FALSE;\r
6229 }\r
6230 \r
6231 /*---------------------------------------------------------------------------*\\r
6232  *\r
6233  * About box dialog functions\r
6234  *\r
6235 \*---------------------------------------------------------------------------*/\r
6236 \r
6237 /* Process messages for "About" dialog box */\r
6238 LRESULT CALLBACK\r
6239 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6240 {\r
6241   switch (message) {\r
6242   case WM_INITDIALOG: /* message: initialize dialog box */\r
6243     /* Center the dialog over the application window */\r
6244     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6245     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6246     Translate(hDlg, ABOUTBOX);\r
6247     JAWS_COPYRIGHT\r
6248     return (TRUE);\r
6249 \r
6250   case WM_COMMAND: /* message: received a command */\r
6251     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6252         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6253       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6254       return (TRUE);\r
6255     }\r
6256     break;\r
6257   }\r
6258   return (FALSE);\r
6259 }\r
6260 \r
6261 /*---------------------------------------------------------------------------*\\r
6262  *\r
6263  * Comment Dialog functions\r
6264  *\r
6265 \*---------------------------------------------------------------------------*/\r
6266 \r
6267 LRESULT CALLBACK\r
6268 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6269 {\r
6270   static HANDLE hwndText = NULL;\r
6271   int len, newSizeX, newSizeY, flags;\r
6272   static int sizeX, sizeY;\r
6273   char *str;\r
6274   RECT rect;\r
6275   MINMAXINFO *mmi;\r
6276 \r
6277   switch (message) {\r
6278   case WM_INITDIALOG: /* message: initialize dialog box */\r
6279     /* Initialize the dialog items */\r
6280     Translate(hDlg, DLG_EditComment);\r
6281     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6282     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6283     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6284     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6285     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6286     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6287     SetWindowText(hDlg, commentTitle);\r
6288     if (editComment) {\r
6289       SetFocus(hwndText);\r
6290     } else {\r
6291       SetFocus(GetDlgItem(hDlg, IDOK));\r
6292     }\r
6293     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6294                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6295                 MAKELPARAM(FALSE, 0));\r
6296     /* Size and position the dialog */\r
6297     if (!commentDialog) {\r
6298       commentDialog = hDlg;\r
6299       flags = SWP_NOZORDER;\r
6300       GetClientRect(hDlg, &rect);\r
6301       sizeX = rect.right;\r
6302       sizeY = rect.bottom;\r
6303       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6304           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6305         WINDOWPLACEMENT wp;\r
6306         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6307         wp.length = sizeof(WINDOWPLACEMENT);\r
6308         wp.flags = 0;\r
6309         wp.showCmd = SW_SHOW;\r
6310         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6311         wp.rcNormalPosition.left = wpComment.x;\r
6312         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6313         wp.rcNormalPosition.top = wpComment.y;\r
6314         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6315         SetWindowPlacement(hDlg, &wp);\r
6316 \r
6317         GetClientRect(hDlg, &rect);\r
6318         newSizeX = rect.right;\r
6319         newSizeY = rect.bottom;\r
6320         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6321                               newSizeX, newSizeY);\r
6322         sizeX = newSizeX;\r
6323         sizeY = newSizeY;\r
6324       }\r
6325     }\r
6326     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6327     return FALSE;\r
6328 \r
6329   case WM_COMMAND: /* message: received a command */\r
6330     switch (LOWORD(wParam)) {\r
6331     case IDOK:\r
6332       if (editComment) {\r
6333         char *p, *q;\r
6334         /* Read changed options from the dialog box */\r
6335         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6336         len = GetWindowTextLength(hwndText);\r
6337         str = (char *) malloc(len + 1);\r
6338         GetWindowText(hwndText, str, len + 1);\r
6339         p = q = str;\r
6340         while (*q) {\r
6341           if (*q == '\r')\r
6342             q++;\r
6343           else\r
6344             *p++ = *q++;\r
6345         }\r
6346         *p = NULLCHAR;\r
6347         ReplaceComment(commentIndex, str);\r
6348         free(str);\r
6349       }\r
6350       CommentPopDown();\r
6351       return TRUE;\r
6352 \r
6353     case IDCANCEL:\r
6354     case OPT_CancelComment:\r
6355       CommentPopDown();\r
6356       return TRUE;\r
6357 \r
6358     case OPT_ClearComment:\r
6359       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6360       break;\r
6361 \r
6362     case OPT_EditComment:\r
6363       EditCommentEvent();\r
6364       return TRUE;\r
6365 \r
6366     default:\r
6367       break;\r
6368     }\r
6369     break;\r
6370 \r
6371   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6372         if( wParam == OPT_CommentText ) {\r
6373             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6374 \r
6375             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6376                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6377                 POINTL pt;\r
6378                 LRESULT index;\r
6379 \r
6380                 pt.x = LOWORD( lpMF->lParam );\r
6381                 pt.y = HIWORD( lpMF->lParam );\r
6382 \r
6383                 if(lpMF->msg == WM_CHAR) {\r
6384                         CHARRANGE sel;\r
6385                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6386                         index = sel.cpMin;\r
6387                 } else\r
6388                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6389 \r
6390                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6391                 len = GetWindowTextLength(hwndText);\r
6392                 str = (char *) malloc(len + 1);\r
6393                 GetWindowText(hwndText, str, len + 1);\r
6394                 ReplaceComment(commentIndex, str);\r
6395                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6396                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6397                 free(str);\r
6398 \r
6399                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6400                 lpMF->msg = WM_USER;\r
6401 \r
6402                 return TRUE;\r
6403             }\r
6404         }\r
6405         break;\r
6406 \r
6407   case WM_SIZE:\r
6408     newSizeX = LOWORD(lParam);\r
6409     newSizeY = HIWORD(lParam);\r
6410     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6411     sizeX = newSizeX;\r
6412     sizeY = newSizeY;\r
6413     break;\r
6414 \r
6415   case WM_GETMINMAXINFO:\r
6416     /* Prevent resizing window too small */\r
6417     mmi = (MINMAXINFO *) lParam;\r
6418     mmi->ptMinTrackSize.x = 100;\r
6419     mmi->ptMinTrackSize.y = 100;\r
6420     break;\r
6421   }\r
6422   return FALSE;\r
6423 }\r
6424 \r
6425 VOID\r
6426 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6427 {\r
6428   FARPROC lpProc;\r
6429   char *p, *q;\r
6430 \r
6431   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6432 \r
6433   if (str == NULL) str = "";\r
6434   p = (char *) malloc(2 * strlen(str) + 2);\r
6435   q = p;\r
6436   while (*str) {\r
6437     if (*str == '\n') *q++ = '\r';\r
6438     *q++ = *str++;\r
6439   }\r
6440   *q = NULLCHAR;\r
6441   if (commentText != NULL) free(commentText);\r
6442 \r
6443   commentIndex = index;\r
6444   commentTitle = title;\r
6445   commentText = p;\r
6446   editComment = edit;\r
6447 \r
6448   if (commentDialog) {\r
6449     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6450     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6451   } else {\r
6452     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6453     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6454                  hwndMain, (DLGPROC)lpProc);\r
6455     FreeProcInstance(lpProc);\r
6456   }\r
6457   commentUp = TRUE;\r
6458 }\r
6459 \r
6460 \r
6461 /*---------------------------------------------------------------------------*\\r
6462  *\r
6463  * Type-in move dialog functions\r
6464  * \r
6465 \*---------------------------------------------------------------------------*/\r
6466 \r
6467 LRESULT CALLBACK\r
6468 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6469 {\r
6470   char move[MSG_SIZ];\r
6471   HWND hInput;\r
6472 \r
6473   switch (message) {\r
6474   case WM_INITDIALOG:\r
6475     move[0] = (char) lParam;\r
6476     move[1] = NULLCHAR;\r
6477     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6478     Translate(hDlg, DLG_TypeInMove);\r
6479     hInput = GetDlgItem(hDlg, OPT_Move);\r
6480     SetWindowText(hInput, move);\r
6481     SetFocus(hInput);\r
6482     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6483     return FALSE;\r
6484 \r
6485   case WM_COMMAND:\r
6486     switch (LOWORD(wParam)) {\r
6487     case IDOK:\r
6488 \r
6489       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6490       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6491       TypeInDoneEvent(move);\r
6492       EndDialog(hDlg, TRUE);\r
6493       return TRUE;\r
6494     case IDCANCEL:\r
6495       EndDialog(hDlg, FALSE);\r
6496       return TRUE;\r
6497     default:\r
6498       break;\r
6499     }\r
6500     break;\r
6501   }\r
6502   return FALSE;\r
6503 }\r
6504 \r
6505 VOID\r
6506 PopUpMoveDialog(char firstchar)\r
6507 {\r
6508     FARPROC lpProc;\r
6509 \r
6510       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6511       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6512         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6513       FreeProcInstance(lpProc);\r
6514 }\r
6515 \r
6516 /*---------------------------------------------------------------------------*\\r
6517  *\r
6518  * Type-in name dialog functions\r
6519  * \r
6520 \*---------------------------------------------------------------------------*/\r
6521 \r
6522 LRESULT CALLBACK\r
6523 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6524 {\r
6525   char move[MSG_SIZ];\r
6526   HWND hInput;\r
6527 \r
6528   switch (message) {\r
6529   case WM_INITDIALOG:\r
6530     move[0] = (char) lParam;\r
6531     move[1] = NULLCHAR;\r
6532     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6533     Translate(hDlg, DLG_TypeInName);\r
6534     hInput = GetDlgItem(hDlg, OPT_Name);\r
6535     SetWindowText(hInput, move);\r
6536     SetFocus(hInput);\r
6537     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6538     return FALSE;\r
6539 \r
6540   case WM_COMMAND:\r
6541     switch (LOWORD(wParam)) {\r
6542     case IDOK:\r
6543       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6544       appData.userName = strdup(move);\r
6545       SetUserLogo();\r
6546       SetGameInfo();\r
6547       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6548         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6549         DisplayTitle(move);\r
6550       }\r
6551 \r
6552 \r
6553       EndDialog(hDlg, TRUE);\r
6554       return TRUE;\r
6555     case IDCANCEL:\r
6556       EndDialog(hDlg, FALSE);\r
6557       return TRUE;\r
6558     default:\r
6559       break;\r
6560     }\r
6561     break;\r
6562   }\r
6563   return FALSE;\r
6564 }\r
6565 \r
6566 VOID\r
6567 PopUpNameDialog(char firstchar)\r
6568 {\r
6569     FARPROC lpProc;\r
6570     \r
6571       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6572       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6573         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6574       FreeProcInstance(lpProc);\r
6575 }\r
6576 \r
6577 /*---------------------------------------------------------------------------*\\r
6578  *\r
6579  *  Error dialogs\r
6580  * \r
6581 \*---------------------------------------------------------------------------*/\r
6582 \r
6583 /* Nonmodal error box */\r
6584 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6585                              WPARAM wParam, LPARAM lParam);\r
6586 \r
6587 VOID\r
6588 ErrorPopUp(char *title, char *content)\r
6589 {\r
6590   FARPROC lpProc;\r
6591   char *p, *q;\r
6592   BOOLEAN modal = hwndMain == NULL;\r
6593 \r
6594   p = content;\r
6595   q = errorMessage;\r
6596   while (*p) {\r
6597     if (*p == '\n') {\r
6598       if (modal) {\r
6599         *q++ = ' ';\r
6600         p++;\r
6601       } else {\r
6602         *q++ = '\r';\r
6603         *q++ = *p++;\r
6604       }\r
6605     } else {\r
6606       *q++ = *p++;\r
6607     }\r
6608   }\r
6609   *q = NULLCHAR;\r
6610   strncpy(errorTitle, title, sizeof(errorTitle));\r
6611   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6612   \r
6613   if (modal) {\r
6614     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6615   } else {\r
6616     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6617     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6618                  hwndMain, (DLGPROC)lpProc);\r
6619     FreeProcInstance(lpProc);\r
6620   }\r
6621 }\r
6622 \r
6623 VOID\r
6624 ErrorPopDown()\r
6625 {\r
6626   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6627   if (errorDialog == NULL) return;\r
6628   DestroyWindow(errorDialog);\r
6629   errorDialog = NULL;\r
6630   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6631 }\r
6632 \r
6633 LRESULT CALLBACK\r
6634 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6635 {\r
6636   HANDLE hwndText;\r
6637   RECT rChild;\r
6638 \r
6639   switch (message) {\r
6640   case WM_INITDIALOG:\r
6641     GetWindowRect(hDlg, &rChild);\r
6642 \r
6643     /*\r
6644     SetWindowPos(hDlg, NULL, rChild.left,\r
6645       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6646       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6647     */\r
6648 \r
6649     /* \r
6650         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6651         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6652         and it doesn't work when you resize the dialog.\r
6653         For now, just give it a default position.\r
6654     */\r
6655     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6656     Translate(hDlg, DLG_Error);\r
6657 \r
6658     errorDialog = hDlg;\r
6659     SetWindowText(hDlg, errorTitle);\r
6660     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6661     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6662     return FALSE;\r
6663 \r
6664   case WM_COMMAND:\r
6665     switch (LOWORD(wParam)) {\r
6666     case IDOK:\r
6667     case IDCANCEL:\r
6668       if (errorDialog == hDlg) errorDialog = NULL;\r
6669       DestroyWindow(hDlg);\r
6670       return TRUE;\r
6671 \r
6672     default:\r
6673       break;\r
6674     }\r
6675     break;\r
6676   }\r
6677   return FALSE;\r
6678 }\r
6679 \r
6680 #ifdef GOTHIC\r
6681 HWND gothicDialog = NULL;\r
6682 \r
6683 LRESULT CALLBACK\r
6684 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6685 {\r
6686   HANDLE hwndText;\r
6687   RECT rChild;\r
6688   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6689 \r
6690   switch (message) {\r
6691   case WM_INITDIALOG:\r
6692     GetWindowRect(hDlg, &rChild);\r
6693 \r
6694     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6695                                                              SWP_NOZORDER);\r
6696 \r
6697     /* \r
6698         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6699         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6700         and it doesn't work when you resize the dialog.\r
6701         For now, just give it a default position.\r
6702     */\r
6703     gothicDialog = hDlg;\r
6704     SetWindowText(hDlg, errorTitle);\r
6705     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6706     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6707     return FALSE;\r
6708 \r
6709   case WM_COMMAND:\r
6710     switch (LOWORD(wParam)) {\r
6711     case IDOK:\r
6712     case IDCANCEL:\r
6713       if (errorDialog == hDlg) errorDialog = NULL;\r
6714       DestroyWindow(hDlg);\r
6715       return TRUE;\r
6716 \r
6717     default:\r
6718       break;\r
6719     }\r
6720     break;\r
6721   }\r
6722   return FALSE;\r
6723 }\r
6724 \r
6725 VOID\r
6726 GothicPopUp(char *title, VariantClass variant)\r
6727 {\r
6728   FARPROC lpProc;\r
6729   static char *lastTitle;\r
6730 \r
6731   strncpy(errorTitle, title, sizeof(errorTitle));\r
6732   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6733 \r
6734   if(lastTitle != title && gothicDialog != NULL) {\r
6735     DestroyWindow(gothicDialog);\r
6736     gothicDialog = NULL;\r
6737   }\r
6738   if(variant != VariantNormal && gothicDialog == NULL) {\r
6739     title = lastTitle;\r
6740     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6741     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6742                  hwndMain, (DLGPROC)lpProc);\r
6743     FreeProcInstance(lpProc);\r
6744   }\r
6745 }\r
6746 #endif\r
6747 \r
6748 /*---------------------------------------------------------------------------*\\r
6749  *\r
6750  *  Ics Interaction console functions\r
6751  *\r
6752 \*---------------------------------------------------------------------------*/\r
6753 \r
6754 #define HISTORY_SIZE 64\r
6755 static char *history[HISTORY_SIZE];\r
6756 int histIn = 0, histP = 0;\r
6757 \r
6758 VOID\r
6759 SaveInHistory(char *cmd)\r
6760 {\r
6761   if (history[histIn] != NULL) {\r
6762     free(history[histIn]);\r
6763     history[histIn] = NULL;\r
6764   }\r
6765   if (*cmd == NULLCHAR) return;\r
6766   history[histIn] = StrSave(cmd);\r
6767   histIn = (histIn + 1) % HISTORY_SIZE;\r
6768   if (history[histIn] != NULL) {\r
6769     free(history[histIn]);\r
6770     history[histIn] = NULL;\r
6771   }\r
6772   histP = histIn;\r
6773 }\r
6774 \r
6775 char *\r
6776 PrevInHistory(char *cmd)\r
6777 {\r
6778   int newhp;\r
6779   if (histP == histIn) {\r
6780     if (history[histIn] != NULL) free(history[histIn]);\r
6781     history[histIn] = StrSave(cmd);\r
6782   }\r
6783   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6784   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6785   histP = newhp;\r
6786   return history[histP];\r
6787 }\r
6788 \r
6789 char *\r
6790 NextInHistory()\r
6791 {\r
6792   if (histP == histIn) return NULL;\r
6793   histP = (histP + 1) % HISTORY_SIZE;\r
6794   return history[histP];   \r
6795 }\r
6796 \r
6797 HMENU\r
6798 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6799 {\r
6800   HMENU hmenu, h;\r
6801   int i = 0;\r
6802   hmenu = LoadMenu(hInst, "TextMenu");\r
6803   h = GetSubMenu(hmenu, 0);\r
6804   while (e->item) {\r
6805     if (strcmp(e->item, "-") == 0) {\r
6806       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6807     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6808       int flags = MF_STRING, j = 0;\r
6809       if (e->item[0] == '|') {\r
6810         flags |= MF_MENUBARBREAK;\r
6811         j++;\r
6812       }\r
6813       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6814       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6815     }\r
6816     e++;\r
6817     i++;\r
6818   } \r
6819   return hmenu;\r
6820 }\r
6821 \r
6822 WNDPROC consoleTextWindowProc;\r
6823 \r
6824 void\r
6825 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6826 {\r
6827   char buf[MSG_SIZ], name[MSG_SIZ];\r
6828   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6829   CHARRANGE sel;\r
6830 \r
6831   if (!getname) {\r
6832     SetWindowText(hInput, command);\r
6833     if (immediate) {\r
6834       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6835     } else {\r
6836       sel.cpMin = 999999;\r
6837       sel.cpMax = 999999;\r
6838       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6839       SetFocus(hInput);\r
6840     }\r
6841     return;\r
6842   }    \r
6843   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6844   if (sel.cpMin == sel.cpMax) {\r
6845     /* Expand to surrounding word */\r
6846     TEXTRANGE tr;\r
6847     do {\r
6848       tr.chrg.cpMax = sel.cpMin;\r
6849       tr.chrg.cpMin = --sel.cpMin;\r
6850       if (sel.cpMin < 0) break;\r
6851       tr.lpstrText = name;\r
6852       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6853     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6854     sel.cpMin++;\r
6855 \r
6856     do {\r
6857       tr.chrg.cpMin = sel.cpMax;\r
6858       tr.chrg.cpMax = ++sel.cpMax;\r
6859       tr.lpstrText = name;\r
6860       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6861     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6862     sel.cpMax--;\r
6863 \r
6864     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6865       MessageBeep(MB_ICONEXCLAMATION);\r
6866       return;\r
6867     }\r
6868     tr.chrg = sel;\r
6869     tr.lpstrText = name;\r
6870     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6871   } else {\r
6872     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6873       MessageBeep(MB_ICONEXCLAMATION);\r
6874       return;\r
6875     }\r
6876     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6877   }\r
6878   if (immediate) {\r
6879     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6880     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6881     SetWindowText(hInput, buf);\r
6882     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6883   } else {\r
6884     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6885       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6886     SetWindowText(hInput, buf);\r
6887     sel.cpMin = 999999;\r
6888     sel.cpMax = 999999;\r
6889     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6890     SetFocus(hInput);\r
6891   }\r
6892 }\r
6893 \r
6894 LRESULT CALLBACK \r
6895 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6896 {\r
6897   HWND hInput;\r
6898   CHARRANGE sel;\r
6899 \r
6900   switch (message) {\r
6901   case WM_KEYDOWN:\r
6902     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6903     if(wParam=='R') return 0;\r
6904     switch (wParam) {\r
6905     case VK_PRIOR:\r
6906       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6907       return 0;\r
6908     case VK_NEXT:\r
6909       sel.cpMin = 999999;\r
6910       sel.cpMax = 999999;\r
6911       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6912       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6913       return 0;\r
6914     }\r
6915     break;\r
6916   case WM_CHAR:\r
6917    if(wParam != '\022') {\r
6918     if (wParam == '\t') {\r
6919       if (GetKeyState(VK_SHIFT) < 0) {\r
6920         /* shifted */\r
6921         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6922         if (buttonDesc[0].hwnd) {\r
6923           SetFocus(buttonDesc[0].hwnd);\r
6924         } else {\r
6925           SetFocus(hwndMain);\r
6926         }\r
6927       } else {\r
6928         /* unshifted */\r
6929         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6930       }\r
6931     } else {\r
6932       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6933       JAWS_DELETE( SetFocus(hInput); )\r
6934       SendMessage(hInput, message, wParam, lParam);\r
6935     }\r
6936     return 0;\r
6937    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6938    lParam = -1;\r
6939   case WM_RBUTTONDOWN:\r
6940     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6941       /* Move selection here if it was empty */\r
6942       POINT pt;\r
6943       pt.x = LOWORD(lParam);\r
6944       pt.y = HIWORD(lParam);\r
6945       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6946       if (sel.cpMin == sel.cpMax) {\r
6947         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6948         sel.cpMax = sel.cpMin;\r
6949         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6950       }\r
6951       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6952 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6953       POINT pt;\r
6954       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6955       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6956       if (sel.cpMin == sel.cpMax) {\r
6957         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6958         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6959       }\r
6960       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6961         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6962       }\r
6963       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6964       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6965       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6966       MenuPopup(hwnd, pt, hmenu, -1);\r
6967 }\r
6968     }\r
6969     return 0;\r
6970   case WM_RBUTTONUP:\r
6971     if (GetKeyState(VK_SHIFT) & ~1) {\r
6972       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6973         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6974     }\r
6975     return 0;\r
6976   case WM_PASTE:\r
6977     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6978     SetFocus(hInput);\r
6979     return SendMessage(hInput, message, wParam, lParam);\r
6980   case WM_MBUTTONDOWN:\r
6981     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6982   case WM_COMMAND:\r
6983     switch (LOWORD(wParam)) {\r
6984     case IDM_QuickPaste:\r
6985       {\r
6986         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6987         if (sel.cpMin == sel.cpMax) {\r
6988           MessageBeep(MB_ICONEXCLAMATION);\r
6989           return 0;\r
6990         }\r
6991         SendMessage(hwnd, WM_COPY, 0, 0);\r
6992         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6993         SendMessage(hInput, WM_PASTE, 0, 0);\r
6994         SetFocus(hInput);\r
6995         return 0;\r
6996       }\r
6997     case IDM_Cut:\r
6998       SendMessage(hwnd, WM_CUT, 0, 0);\r
6999       return 0;\r
7000     case IDM_Paste:\r
7001       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7002       return 0;\r
7003     case IDM_Copy:\r
7004       SendMessage(hwnd, WM_COPY, 0, 0);\r
7005       return 0;\r
7006     default:\r
7007       {\r
7008         int i = LOWORD(wParam) - IDM_CommandX;\r
7009         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7010             icsTextMenuEntry[i].command != NULL) {\r
7011           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7012                    icsTextMenuEntry[i].getname,\r
7013                    icsTextMenuEntry[i].immediate);\r
7014           return 0;\r
7015         }\r
7016       }\r
7017       break;\r
7018     }\r
7019     break;\r
7020   }\r
7021   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7022 }\r
7023 \r
7024 WNDPROC consoleInputWindowProc;\r
7025 \r
7026 LRESULT CALLBACK\r
7027 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7028 {\r
7029   char buf[MSG_SIZ];\r
7030   char *p;\r
7031   static BOOL sendNextChar = FALSE;\r
7032   static BOOL quoteNextChar = FALSE;\r
7033   InputSource *is = consoleInputSource;\r
7034   CHARFORMAT cf;\r
7035   CHARRANGE sel;\r
7036 \r
7037   switch (message) {\r
7038   case WM_CHAR:\r
7039     if (!appData.localLineEditing || sendNextChar) {\r
7040       is->buf[0] = (CHAR) wParam;\r
7041       is->count = 1;\r
7042       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7043       sendNextChar = FALSE;\r
7044       return 0;\r
7045     }\r
7046     if (quoteNextChar) {\r
7047       buf[0] = (char) wParam;\r
7048       buf[1] = NULLCHAR;\r
7049       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7050       quoteNextChar = FALSE;\r
7051       return 0;\r
7052     }\r
7053     switch (wParam) {\r
7054     case '\r':   /* Enter key */\r
7055       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7056       if (consoleEcho) SaveInHistory(is->buf);\r
7057       is->buf[is->count++] = '\n';\r
7058       is->buf[is->count] = NULLCHAR;\r
7059       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7060       if (consoleEcho) {\r
7061         ConsoleOutput(is->buf, is->count, TRUE);\r
7062       } else if (appData.localLineEditing) {\r
7063         ConsoleOutput("\n", 1, TRUE);\r
7064       }\r
7065       /* fall thru */\r
7066     case '\033': /* Escape key */\r
7067       SetWindowText(hwnd, "");\r
7068       cf.cbSize = sizeof(CHARFORMAT);\r
7069       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7070       if (consoleEcho) {\r
7071         cf.crTextColor = textAttribs[ColorNormal].color;\r
7072       } else {\r
7073         cf.crTextColor = COLOR_ECHOOFF;\r
7074       }\r
7075       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7076       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7077       return 0;\r
7078     case '\t':   /* Tab key */\r
7079       if (GetKeyState(VK_SHIFT) < 0) {\r
7080         /* shifted */\r
7081         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7082       } else {\r
7083         /* unshifted */\r
7084         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7085         if (buttonDesc[0].hwnd) {\r
7086           SetFocus(buttonDesc[0].hwnd);\r
7087         } else {\r
7088           SetFocus(hwndMain);\r
7089         }\r
7090       }\r
7091       return 0;\r
7092     case '\023': /* Ctrl+S */\r
7093       sendNextChar = TRUE;\r
7094       return 0;\r
7095     case '\021': /* Ctrl+Q */\r
7096       quoteNextChar = TRUE;\r
7097       return 0;\r
7098     JAWS_REPLAY\r
7099     default:\r
7100       break;\r
7101     }\r
7102     break;\r
7103   case WM_KEYDOWN:\r
7104     switch (wParam) {\r
7105     case VK_UP:\r
7106       GetWindowText(hwnd, buf, MSG_SIZ);\r
7107       p = PrevInHistory(buf);\r
7108       if (p != NULL) {\r
7109         SetWindowText(hwnd, p);\r
7110         sel.cpMin = 999999;\r
7111         sel.cpMax = 999999;\r
7112         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7113         return 0;\r
7114       }\r
7115       break;\r
7116     case VK_DOWN:\r
7117       p = NextInHistory();\r
7118       if (p != NULL) {\r
7119         SetWindowText(hwnd, p);\r
7120         sel.cpMin = 999999;\r
7121         sel.cpMax = 999999;\r
7122         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7123         return 0;\r
7124       }\r
7125       break;\r
7126     case VK_HOME:\r
7127     case VK_END:\r
7128       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7129       /* fall thru */\r
7130     case VK_PRIOR:\r
7131     case VK_NEXT:\r
7132       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7133       return 0;\r
7134     }\r
7135     break;\r
7136   case WM_MBUTTONDOWN:\r
7137     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7138       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7139     break;\r
7140   case WM_RBUTTONUP:\r
7141     if (GetKeyState(VK_SHIFT) & ~1) {\r
7142       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7143         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7144     } else {\r
7145       POINT pt;\r
7146       HMENU hmenu;\r
7147       hmenu = LoadMenu(hInst, "InputMenu");\r
7148       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7149       if (sel.cpMin == sel.cpMax) {\r
7150         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7151         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7152       }\r
7153       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7154         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7155       }\r
7156       pt.x = LOWORD(lParam);\r
7157       pt.y = HIWORD(lParam);\r
7158       MenuPopup(hwnd, pt, hmenu, -1);\r
7159     }\r
7160     return 0;\r
7161   case WM_COMMAND:\r
7162     switch (LOWORD(wParam)) { \r
7163     case IDM_Undo:\r
7164       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7165       return 0;\r
7166     case IDM_SelectAll:\r
7167       sel.cpMin = 0;\r
7168       sel.cpMax = -1; /*999999?*/\r
7169       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7170       return 0;\r
7171     case IDM_Cut:\r
7172       SendMessage(hwnd, WM_CUT, 0, 0);\r
7173       return 0;\r
7174     case IDM_Paste:\r
7175       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7176       return 0;\r
7177     case IDM_Copy:\r
7178       SendMessage(hwnd, WM_COPY, 0, 0);\r
7179       return 0;\r
7180     }\r
7181     break;\r
7182   }\r
7183   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7184 }\r
7185 \r
7186 #define CO_MAX  100000\r
7187 #define CO_TRIM   1000\r
7188 \r
7189 LRESULT CALLBACK\r
7190 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7191 {\r
7192   static SnapData sd;\r
7193   HWND hText, hInput;\r
7194   RECT rect;\r
7195   static int sizeX, sizeY;\r
7196   int newSizeX, newSizeY;\r
7197   MINMAXINFO *mmi;\r
7198   WORD wMask;\r
7199 \r
7200   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7201   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7202 \r
7203   switch (message) {\r
7204   case WM_NOTIFY:\r
7205     if (((NMHDR*)lParam)->code == EN_LINK)\r
7206     {\r
7207       ENLINK *pLink = (ENLINK*)lParam;\r
7208       if (pLink->msg == WM_LBUTTONUP)\r
7209       {\r
7210         TEXTRANGE tr;\r
7211 \r
7212         tr.chrg = pLink->chrg;\r
7213         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7214         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7215         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7216         free(tr.lpstrText);\r
7217       }\r
7218     }\r
7219     break;\r
7220   case WM_INITDIALOG: /* message: initialize dialog box */\r
7221     hwndConsole = hDlg;\r
7222     SetFocus(hInput);\r
7223     consoleTextWindowProc = (WNDPROC)\r
7224       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7225     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7226     consoleInputWindowProc = (WNDPROC)\r
7227       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7228     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7229     Colorize(ColorNormal, TRUE);\r
7230     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7231     ChangedConsoleFont();\r
7232     GetClientRect(hDlg, &rect);\r
7233     sizeX = rect.right;\r
7234     sizeY = rect.bottom;\r
7235     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7236         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7237       WINDOWPLACEMENT wp;\r
7238       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7239       wp.length = sizeof(WINDOWPLACEMENT);\r
7240       wp.flags = 0;\r
7241       wp.showCmd = SW_SHOW;\r
7242       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7243       wp.rcNormalPosition.left = wpConsole.x;\r
7244       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7245       wp.rcNormalPosition.top = wpConsole.y;\r
7246       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7247       SetWindowPlacement(hDlg, &wp);\r
7248     }\r
7249 \r
7250    // [HGM] Chessknight's change 2004-07-13\r
7251    else { /* Determine Defaults */\r
7252        WINDOWPLACEMENT wp;\r
7253        wpConsole.x = wpMain.width + 1;\r
7254        wpConsole.y = wpMain.y;\r
7255        wpConsole.width = screenWidth -  wpMain.width;\r
7256        wpConsole.height = wpMain.height;\r
7257        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7258        wp.length = sizeof(WINDOWPLACEMENT);\r
7259        wp.flags = 0;\r
7260        wp.showCmd = SW_SHOW;\r
7261        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7262        wp.rcNormalPosition.left = wpConsole.x;\r
7263        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7264        wp.rcNormalPosition.top = wpConsole.y;\r
7265        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7266        SetWindowPlacement(hDlg, &wp);\r
7267     }\r
7268 \r
7269    // Allow hText to highlight URLs and send notifications on them\r
7270    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7271    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7272    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7273    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7274 \r
7275     return FALSE;\r
7276 \r
7277   case WM_SETFOCUS:\r
7278     SetFocus(hInput);\r
7279     return 0;\r
7280 \r
7281   case WM_CLOSE:\r
7282     ExitEvent(0);\r
7283     /* not reached */\r
7284     break;\r
7285 \r
7286   case WM_SIZE:\r
7287     if (IsIconic(hDlg)) break;\r
7288     newSizeX = LOWORD(lParam);\r
7289     newSizeY = HIWORD(lParam);\r
7290     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7291       RECT rectText, rectInput;\r
7292       POINT pt;\r
7293       int newTextHeight, newTextWidth;\r
7294       GetWindowRect(hText, &rectText);\r
7295       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7296       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7297       if (newTextHeight < 0) {\r
7298         newSizeY += -newTextHeight;\r
7299         newTextHeight = 0;\r
7300       }\r
7301       SetWindowPos(hText, NULL, 0, 0,\r
7302         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7303       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7304       pt.x = rectInput.left;\r
7305       pt.y = rectInput.top + newSizeY - sizeY;\r
7306       ScreenToClient(hDlg, &pt);\r
7307       SetWindowPos(hInput, NULL, \r
7308         pt.x, pt.y, /* needs client coords */   \r
7309         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7310         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7311     }\r
7312     sizeX = newSizeX;\r
7313     sizeY = newSizeY;\r
7314     break;\r
7315 \r
7316   case WM_GETMINMAXINFO:\r
7317     /* Prevent resizing window too small */\r
7318     mmi = (MINMAXINFO *) lParam;\r
7319     mmi->ptMinTrackSize.x = 100;\r
7320     mmi->ptMinTrackSize.y = 100;\r
7321     break;\r
7322 \r
7323   /* [AS] Snapping */\r
7324   case WM_ENTERSIZEMOVE:\r
7325     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7326 \r
7327   case WM_SIZING:\r
7328     return OnSizing( &sd, hDlg, wParam, lParam );\r
7329 \r
7330   case WM_MOVING:\r
7331     return OnMoving( &sd, hDlg, wParam, lParam );\r
7332 \r
7333   case WM_EXITSIZEMOVE:\r
7334         UpdateICSWidth(hText);\r
7335     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7336   }\r
7337 \r
7338   return DefWindowProc(hDlg, message, wParam, lParam);\r
7339 }\r
7340 \r
7341 \r
7342 VOID\r
7343 ConsoleCreate()\r
7344 {\r
7345   HWND hCons;\r
7346   if (hwndConsole) return;\r
7347   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7348   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7349 }\r
7350 \r
7351 \r
7352 VOID\r
7353 ConsoleOutput(char* data, int length, int forceVisible)\r
7354 {\r
7355   HWND hText;\r
7356   int trim, exlen;\r
7357   char *p, *q;\r
7358   char buf[CO_MAX+1];\r
7359   POINT pEnd;\r
7360   RECT rect;\r
7361   static int delayLF = 0;\r
7362   CHARRANGE savesel, sel;\r
7363 \r
7364   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7365   p = data;\r
7366   q = buf;\r
7367   if (delayLF) {\r
7368     *q++ = '\r';\r
7369     *q++ = '\n';\r
7370     delayLF = 0;\r
7371   }\r
7372   while (length--) {\r
7373     if (*p == '\n') {\r
7374       if (*++p) {\r
7375         *q++ = '\r';\r
7376         *q++ = '\n';\r
7377       } else {\r
7378         delayLF = 1;\r
7379       }\r
7380     } else if (*p == '\007') {\r
7381        MyPlaySound(&sounds[(int)SoundBell]);\r
7382        p++;\r
7383     } else {\r
7384       *q++ = *p++;\r
7385     }\r
7386   }\r
7387   *q = NULLCHAR;\r
7388   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7389   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7390   /* Save current selection */\r
7391   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7392   exlen = GetWindowTextLength(hText);\r
7393   /* Find out whether current end of text is visible */\r
7394   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7395   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7396   /* Trim existing text if it's too long */\r
7397   if (exlen + (q - buf) > CO_MAX) {\r
7398     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7399     sel.cpMin = 0;\r
7400     sel.cpMax = trim;\r
7401     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7402     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7403     exlen -= trim;\r
7404     savesel.cpMin -= trim;\r
7405     savesel.cpMax -= trim;\r
7406     if (exlen < 0) exlen = 0;\r
7407     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7408     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7409   }\r
7410   /* Append the new text */\r
7411   sel.cpMin = exlen;\r
7412   sel.cpMax = exlen;\r
7413   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7414   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7415   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7416   if (forceVisible || exlen == 0 ||\r
7417       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7418        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7419     /* Scroll to make new end of text visible if old end of text\r
7420        was visible or new text is an echo of user typein */\r
7421     sel.cpMin = 9999999;\r
7422     sel.cpMax = 9999999;\r
7423     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7424     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7425     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7426     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7427   }\r
7428   if (savesel.cpMax == exlen || forceVisible) {\r
7429     /* Move insert point to new end of text if it was at the old\r
7430        end of text or if the new text is an echo of user typein */\r
7431     sel.cpMin = 9999999;\r
7432     sel.cpMax = 9999999;\r
7433     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7434   } else {\r
7435     /* Restore previous selection */\r
7436     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7437   }\r
7438   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7439 }\r
7440 \r
7441 /*---------*/\r
7442 \r
7443 \r
7444 void\r
7445 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7446 {\r
7447   char buf[100];\r
7448   char *str;\r
7449   COLORREF oldFg, oldBg;\r
7450   HFONT oldFont;\r
7451   RECT rect;\r
7452 \r
7453   if(copyNumber > 1)\r
7454     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7455 \r
7456   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7457   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7458   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7459 \r
7460   rect.left = x;\r
7461   rect.right = x + squareSize;\r
7462   rect.top  = y;\r
7463   rect.bottom = y + squareSize;\r
7464   str = buf;\r
7465 \r
7466   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7467                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7468              y, ETO_CLIPPED|ETO_OPAQUE,\r
7469              &rect, str, strlen(str), NULL);\r
7470 \r
7471   (void) SetTextColor(hdc, oldFg);\r
7472   (void) SetBkColor(hdc, oldBg);\r
7473   (void) SelectObject(hdc, oldFont);\r
7474 }\r
7475 \r
7476 void\r
7477 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7478               RECT *rect, char *color, char *flagFell)\r
7479 {\r
7480   char buf[100];\r
7481   char *str;\r
7482   COLORREF oldFg, oldBg;\r
7483   HFONT oldFont;\r
7484 \r
7485   if (twoBoards && partnerUp) return;\r
7486   if (appData.clockMode) {\r
7487     if (tinyLayout)\r
7488       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7489     else\r
7490       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7491     str = buf;\r
7492   } else {\r
7493     str = color;\r
7494   }\r
7495 \r
7496   if (highlight) {\r
7497     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7498     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7499   } else {\r
7500     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7501     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7502   }\r
7503   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7504 \r
7505   JAWS_SILENCE\r
7506 \r
7507   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7508              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7509              rect, str, strlen(str), NULL);\r
7510   if(logoHeight > 0 && appData.clockMode) {\r
7511       RECT r;\r
7512       str += strlen(color)+2;\r
7513       r.top = rect->top + logoHeight/2;\r
7514       r.left = rect->left;\r
7515       r.right = rect->right;\r
7516       r.bottom = rect->bottom;\r
7517       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7518                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7519                  &r, str, strlen(str), NULL);\r
7520   }\r
7521   (void) SetTextColor(hdc, oldFg);\r
7522   (void) SetBkColor(hdc, oldBg);\r
7523   (void) SelectObject(hdc, oldFont);\r
7524 }\r
7525 \r
7526 \r
7527 int\r
7528 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7529            OVERLAPPED *ovl)\r
7530 {\r
7531   int ok, err;\r
7532 \r
7533   /* [AS]  */\r
7534   if( count <= 0 ) {\r
7535     if (appData.debugMode) {\r
7536       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7537     }\r
7538 \r
7539     return ERROR_INVALID_USER_BUFFER;\r
7540   }\r
7541 \r
7542   ResetEvent(ovl->hEvent);\r
7543   ovl->Offset = ovl->OffsetHigh = 0;\r
7544   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7545   if (ok) {\r
7546     err = NO_ERROR;\r
7547   } else {\r
7548     err = GetLastError();\r
7549     if (err == ERROR_IO_PENDING) {\r
7550       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7551       if (ok)\r
7552         err = NO_ERROR;\r
7553       else\r
7554         err = GetLastError();\r
7555     }\r
7556   }\r
7557   return err;\r
7558 }\r
7559 \r
7560 int\r
7561 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7562             OVERLAPPED *ovl)\r
7563 {\r
7564   int ok, err;\r
7565 \r
7566   ResetEvent(ovl->hEvent);\r
7567   ovl->Offset = ovl->OffsetHigh = 0;\r
7568   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7569   if (ok) {\r
7570     err = NO_ERROR;\r
7571   } else {\r
7572     err = GetLastError();\r
7573     if (err == ERROR_IO_PENDING) {\r
7574       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7575       if (ok)\r
7576         err = NO_ERROR;\r
7577       else\r
7578         err = GetLastError();\r
7579     }\r
7580   }\r
7581   return err;\r
7582 }\r
7583 \r
7584 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7585 void CheckForInputBufferFull( InputSource * is )\r
7586 {\r
7587     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7588         /* Look for end of line */\r
7589         char * p = is->buf;\r
7590         \r
7591         while( p < is->next && *p != '\n' ) {\r
7592             p++;\r
7593         }\r
7594 \r
7595         if( p >= is->next ) {\r
7596             if (appData.debugMode) {\r
7597                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7598             }\r
7599 \r
7600             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7601             is->count = (DWORD) -1;\r
7602             is->next = is->buf;\r
7603         }\r
7604     }\r
7605 }\r
7606 \r
7607 DWORD\r
7608 InputThread(LPVOID arg)\r
7609 {\r
7610   InputSource *is;\r
7611   OVERLAPPED ovl;\r
7612 \r
7613   is = (InputSource *) arg;\r
7614   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7615   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7616   while (is->hThread != NULL) {\r
7617     is->error = DoReadFile(is->hFile, is->next,\r
7618                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7619                            &is->count, &ovl);\r
7620     if (is->error == NO_ERROR) {\r
7621       is->next += is->count;\r
7622     } else {\r
7623       if (is->error == ERROR_BROKEN_PIPE) {\r
7624         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7625         is->count = 0;\r
7626       } else {\r
7627         is->count = (DWORD) -1;\r
7628         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7629         break; \r
7630       }\r
7631     }\r
7632 \r
7633     CheckForInputBufferFull( is );\r
7634 \r
7635     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7636 \r
7637     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7638 \r
7639     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7640   }\r
7641 \r
7642   CloseHandle(ovl.hEvent);\r
7643   CloseHandle(is->hFile);\r
7644 \r
7645   if (appData.debugMode) {\r
7646     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7647   }\r
7648 \r
7649   return 0;\r
7650 }\r
7651 \r
7652 \r
7653 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7654 DWORD\r
7655 NonOvlInputThread(LPVOID arg)\r
7656 {\r
7657   InputSource *is;\r
7658   char *p, *q;\r
7659   int i;\r
7660   char prev;\r
7661 \r
7662   is = (InputSource *) arg;\r
7663   while (is->hThread != NULL) {\r
7664     is->error = ReadFile(is->hFile, is->next,\r
7665                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7666                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7667     if (is->error == NO_ERROR) {\r
7668       /* Change CRLF to LF */\r
7669       if (is->next > is->buf) {\r
7670         p = is->next - 1;\r
7671         i = is->count + 1;\r
7672       } else {\r
7673         p = is->next;\r
7674         i = is->count;\r
7675       }\r
7676       q = p;\r
7677       prev = NULLCHAR;\r
7678       while (i > 0) {\r
7679         if (prev == '\r' && *p == '\n') {\r
7680           *(q-1) = '\n';\r
7681           is->count--;\r
7682         } else { \r
7683           *q++ = *p;\r
7684         }\r
7685         prev = *p++;\r
7686         i--;\r
7687       }\r
7688       *q = NULLCHAR;\r
7689       is->next = q;\r
7690     } else {\r
7691       if (is->error == ERROR_BROKEN_PIPE) {\r
7692         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7693         is->count = 0; \r
7694       } else {\r
7695         is->count = (DWORD) -1;\r
7696       }\r
7697     }\r
7698 \r
7699     CheckForInputBufferFull( is );\r
7700 \r
7701     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7702 \r
7703     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7704 \r
7705     if (is->count < 0) break;  /* Quit on error */\r
7706   }\r
7707   CloseHandle(is->hFile);\r
7708   return 0;\r
7709 }\r
7710 \r
7711 DWORD\r
7712 SocketInputThread(LPVOID arg)\r
7713 {\r
7714   InputSource *is;\r
7715 \r
7716   is = (InputSource *) arg;\r
7717   while (is->hThread != NULL) {\r
7718     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7719     if ((int)is->count == SOCKET_ERROR) {\r
7720       is->count = (DWORD) -1;\r
7721       is->error = WSAGetLastError();\r
7722     } else {\r
7723       is->error = NO_ERROR;\r
7724       is->next += is->count;\r
7725       if (is->count == 0 && is->second == is) {\r
7726         /* End of file on stderr; quit with no message */\r
7727         break;\r
7728       }\r
7729     }\r
7730     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7731 \r
7732     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7733 \r
7734     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7735   }\r
7736   return 0;\r
7737 }\r
7738 \r
7739 VOID\r
7740 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7741 {\r
7742   InputSource *is;\r
7743 \r
7744   is = (InputSource *) lParam;\r
7745   if (is->lineByLine) {\r
7746     /* Feed in lines one by one */\r
7747     char *p = is->buf;\r
7748     char *q = p;\r
7749     while (q < is->next) {\r
7750       if (*q++ == '\n') {\r
7751         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7752         p = q;\r
7753       }\r
7754     }\r
7755     \r
7756     /* Move any partial line to the start of the buffer */\r
7757     q = is->buf;\r
7758     while (p < is->next) {\r
7759       *q++ = *p++;\r
7760     }\r
7761     is->next = q;\r
7762 \r
7763     if (is->error != NO_ERROR || is->count == 0) {\r
7764       /* Notify backend of the error.  Note: If there was a partial\r
7765          line at the end, it is not flushed through. */\r
7766       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7767     }\r
7768   } else {\r
7769     /* Feed in the whole chunk of input at once */\r
7770     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7771     is->next = is->buf;\r
7772   }\r
7773 }\r
7774 \r
7775 /*---------------------------------------------------------------------------*\\r
7776  *\r
7777  *  Menu enables. Used when setting various modes.\r
7778  *\r
7779 \*---------------------------------------------------------------------------*/\r
7780 \r
7781 typedef struct {\r
7782   int item;\r
7783   int flags;\r
7784 } Enables;\r
7785 \r
7786 VOID\r
7787 GreyRevert(Boolean grey)\r
7788 { // [HGM] vari: for retracting variations in local mode\r
7789   HMENU hmenu = GetMenu(hwndMain);\r
7790   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7791   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7792 }\r
7793 \r
7794 VOID\r
7795 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7796 {\r
7797   while (enab->item > 0) {\r
7798     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7799     enab++;\r
7800   }\r
7801 }\r
7802 \r
7803 Enables gnuEnables[] = {\r
7804   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7817 \r
7818   // Needed to switch from ncp to GNU mode on Engine Load\r
7819   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7820   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7822   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7823   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7824   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7833   { -1, -1 }\r
7834 };\r
7835 \r
7836 Enables icsEnables[] = {\r
7837   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7857   { -1, -1 }\r
7858 };\r
7859 \r
7860 #if ZIPPY\r
7861 Enables zippyEnables[] = {\r
7862   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7866   { -1, -1 }\r
7867 };\r
7868 #endif\r
7869 \r
7870 Enables ncpEnables[] = {\r
7871   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7880   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7881   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7893   { -1, -1 }\r
7894 };\r
7895 \r
7896 Enables trainingOnEnables[] = {\r
7897   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7906   { -1, -1 }\r
7907 };\r
7908 \r
7909 Enables trainingOffEnables[] = {\r
7910   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7919   { -1, -1 }\r
7920 };\r
7921 \r
7922 /* These modify either ncpEnables or gnuEnables */\r
7923 Enables cmailEnables[] = {\r
7924   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7926   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7927   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7931   { -1, -1 }\r
7932 };\r
7933 \r
7934 Enables machineThinkingEnables[] = {\r
7935   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7948 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7951   { -1, -1 }\r
7952 };\r
7953 \r
7954 Enables userThinkingEnables[] = {\r
7955   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7960   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7961   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7962   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7963   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7964   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7965   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7967   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7968 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7969   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7971   { -1, -1 }\r
7972 };\r
7973 \r
7974 /*---------------------------------------------------------------------------*\\r
7975  *\r
7976  *  Front-end interface functions exported by XBoard.\r
7977  *  Functions appear in same order as prototypes in frontend.h.\r
7978  * \r
7979 \*---------------------------------------------------------------------------*/\r
7980 VOID\r
7981 CheckMark(UINT item, int state)\r
7982 {\r
7983     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7984 }\r
7985 \r
7986 VOID\r
7987 ModeHighlight()\r
7988 {\r
7989   static UINT prevChecked = 0;\r
7990   static int prevPausing = 0;\r
7991   UINT nowChecked;\r
7992 \r
7993   if (pausing != prevPausing) {\r
7994     prevPausing = pausing;\r
7995     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7996                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7997     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7998   }\r
7999 \r
8000   switch (gameMode) {\r
8001   case BeginningOfGame:\r
8002     if (appData.icsActive)\r
8003       nowChecked = IDM_IcsClient;\r
8004     else if (appData.noChessProgram)\r
8005       nowChecked = IDM_EditGame;\r
8006     else\r
8007       nowChecked = IDM_MachineBlack;\r
8008     break;\r
8009   case MachinePlaysBlack:\r
8010     nowChecked = IDM_MachineBlack;\r
8011     break;\r
8012   case MachinePlaysWhite:\r
8013     nowChecked = IDM_MachineWhite;\r
8014     break;\r
8015   case TwoMachinesPlay:\r
8016     nowChecked = IDM_TwoMachines;\r
8017     break;\r
8018   case AnalyzeMode:\r
8019     nowChecked = IDM_AnalysisMode;\r
8020     break;\r
8021   case AnalyzeFile:\r
8022     nowChecked = IDM_AnalyzeFile;\r
8023     break;\r
8024   case EditGame:\r
8025     nowChecked = IDM_EditGame;\r
8026     break;\r
8027   case PlayFromGameFile:\r
8028     nowChecked = IDM_LoadGame;\r
8029     break;\r
8030   case EditPosition:\r
8031     nowChecked = IDM_EditPosition;\r
8032     break;\r
8033   case Training:\r
8034     nowChecked = IDM_Training;\r
8035     break;\r
8036   case IcsPlayingWhite:\r
8037   case IcsPlayingBlack:\r
8038   case IcsObserving:\r
8039   case IcsIdle:\r
8040     nowChecked = IDM_IcsClient;\r
8041     break;\r
8042   default:\r
8043   case EndOfGame:\r
8044     nowChecked = 0;\r
8045     break;\r
8046   }\r
8047   CheckMark(prevChecked, MF_UNCHECKED);\r
8048   CheckMark(nowChecked, MF_CHECKED);\r
8049   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8050 \r
8051   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8052     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8053                           MF_BYCOMMAND|MF_ENABLED);\r
8054   } else {\r
8055     (void) EnableMenuItem(GetMenu(hwndMain), \r
8056                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8057   }\r
8058 \r
8059   prevChecked = nowChecked;\r
8060 \r
8061   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8062   if (appData.icsActive) {\r
8063        if (appData.icsEngineAnalyze) {\r
8064                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8065        } else {\r
8066                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8067        }\r
8068   }\r
8069   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8070 }\r
8071 \r
8072 VOID\r
8073 SetICSMode()\r
8074 {\r
8075   HMENU hmenu = GetMenu(hwndMain);\r
8076   SetMenuEnables(hmenu, icsEnables);\r
8077   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8078     MF_BYCOMMAND|MF_ENABLED);\r
8079 #if ZIPPY\r
8080   if (appData.zippyPlay) {\r
8081     SetMenuEnables(hmenu, zippyEnables);\r
8082     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8083          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8084           MF_BYCOMMAND|MF_ENABLED);\r
8085   }\r
8086 #endif\r
8087 }\r
8088 \r
8089 VOID\r
8090 SetGNUMode()\r
8091 {\r
8092   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8093 }\r
8094 \r
8095 VOID\r
8096 SetNCPMode()\r
8097 {\r
8098   HMENU hmenu = GetMenu(hwndMain);\r
8099   SetMenuEnables(hmenu, ncpEnables);\r
8100     DrawMenuBar(hwndMain);\r
8101 }\r
8102 \r
8103 VOID\r
8104 SetCmailMode()\r
8105 {\r
8106   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8107 }\r
8108 \r
8109 VOID \r
8110 SetTrainingModeOn()\r
8111 {\r
8112   int i;\r
8113   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8114   for (i = 0; i < N_BUTTONS; i++) {\r
8115     if (buttonDesc[i].hwnd != NULL)\r
8116       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8117   }\r
8118   CommentPopDown();\r
8119 }\r
8120 \r
8121 VOID SetTrainingModeOff()\r
8122 {\r
8123   int i;\r
8124   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8125   for (i = 0; i < N_BUTTONS; i++) {\r
8126     if (buttonDesc[i].hwnd != NULL)\r
8127       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8128   }\r
8129 }\r
8130 \r
8131 \r
8132 VOID\r
8133 SetUserThinkingEnables()\r
8134 {\r
8135   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8136 }\r
8137 \r
8138 VOID\r
8139 SetMachineThinkingEnables()\r
8140 {\r
8141   HMENU hMenu = GetMenu(hwndMain);\r
8142   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8143 \r
8144   SetMenuEnables(hMenu, machineThinkingEnables);\r
8145 \r
8146   if (gameMode == MachinePlaysBlack) {\r
8147     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8148   } else if (gameMode == MachinePlaysWhite) {\r
8149     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8150   } else if (gameMode == TwoMachinesPlay) {\r
8151     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8152   }\r
8153 }\r
8154 \r
8155 \r
8156 VOID\r
8157 DisplayTitle(char *str)\r
8158 {\r
8159   char title[MSG_SIZ], *host;\r
8160   if (str[0] != NULLCHAR) {\r
8161     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8162   } else if (appData.icsActive) {\r
8163     if (appData.icsCommPort[0] != NULLCHAR)\r
8164       host = "ICS";\r
8165     else \r
8166       host = appData.icsHost;\r
8167       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8168   } else if (appData.noChessProgram) {\r
8169     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8170   } else {\r
8171     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8172     strcat(title, ": ");\r
8173     strcat(title, first.tidy);\r
8174   }\r
8175   SetWindowText(hwndMain, title);\r
8176 }\r
8177 \r
8178 \r
8179 VOID\r
8180 DisplayMessage(char *str1, char *str2)\r
8181 {\r
8182   HDC hdc;\r
8183   HFONT oldFont;\r
8184   int remain = MESSAGE_TEXT_MAX - 1;\r
8185   int len;\r
8186 \r
8187   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8188   messageText[0] = NULLCHAR;\r
8189   if (*str1) {\r
8190     len = strlen(str1);\r
8191     if (len > remain) len = remain;\r
8192     strncpy(messageText, str1, len);\r
8193     messageText[len] = NULLCHAR;\r
8194     remain -= len;\r
8195   }\r
8196   if (*str2 && remain >= 2) {\r
8197     if (*str1) {\r
8198       strcat(messageText, "  ");\r
8199       remain -= 2;\r
8200     }\r
8201     len = strlen(str2);\r
8202     if (len > remain) len = remain;\r
8203     strncat(messageText, str2, len);\r
8204   }\r
8205   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8206   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8207 \r
8208   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8209 \r
8210   SAYMACHINEMOVE();\r
8211 \r
8212   hdc = GetDC(hwndMain);\r
8213   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8214   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8215              &messageRect, messageText, strlen(messageText), NULL);\r
8216   (void) SelectObject(hdc, oldFont);\r
8217   (void) ReleaseDC(hwndMain, hdc);\r
8218 }\r
8219 \r
8220 VOID\r
8221 DisplayError(char *str, int error)\r
8222 {\r
8223   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8224   int len;\r
8225 \r
8226   if (error == 0) {\r
8227     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8228   } else {\r
8229     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8230                         NULL, error, LANG_NEUTRAL,\r
8231                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8232     if (len > 0) {\r
8233       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8234     } else {\r
8235       ErrorMap *em = errmap;\r
8236       while (em->err != 0 && em->err != error) em++;\r
8237       if (em->err != 0) {\r
8238         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8239       } else {\r
8240         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8241       }\r
8242     }\r
8243   }\r
8244   \r
8245   ErrorPopUp(_("Error"), buf);\r
8246 }\r
8247 \r
8248 \r
8249 VOID\r
8250 DisplayMoveError(char *str)\r
8251 {\r
8252   fromX = fromY = -1;\r
8253   ClearHighlights();\r
8254   DrawPosition(FALSE, NULL);\r
8255   if (appData.popupMoveErrors) {\r
8256     ErrorPopUp(_("Error"), str);\r
8257   } else {\r
8258     DisplayMessage(str, "");\r
8259     moveErrorMessageUp = TRUE;\r
8260   }\r
8261 }\r
8262 \r
8263 VOID\r
8264 DisplayFatalError(char *str, int error, int exitStatus)\r
8265 {\r
8266   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8267   int len;\r
8268   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8269 \r
8270   if (error != 0) {\r
8271     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8272                         NULL, error, LANG_NEUTRAL,\r
8273                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8274     if (len > 0) {\r
8275       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8276     } else {\r
8277       ErrorMap *em = errmap;\r
8278       while (em->err != 0 && em->err != error) em++;\r
8279       if (em->err != 0) {\r
8280         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8281       } else {\r
8282         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8283       }\r
8284     }\r
8285     str = buf;\r
8286   }\r
8287   if (appData.debugMode) {\r
8288     fprintf(debugFP, "%s: %s\n", label, str);\r
8289   }\r
8290   if (appData.popupExitMessage) {\r
8291     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8292                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8293   }\r
8294   ExitEvent(exitStatus);\r
8295 }\r
8296 \r
8297 \r
8298 VOID\r
8299 DisplayInformation(char *str)\r
8300 {\r
8301   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8302 }\r
8303 \r
8304 \r
8305 VOID\r
8306 DisplayNote(char *str)\r
8307 {\r
8308   ErrorPopUp(_("Note"), str);\r
8309 }\r
8310 \r
8311 \r
8312 typedef struct {\r
8313   char *title, *question, *replyPrefix;\r
8314   ProcRef pr;\r
8315 } QuestionParams;\r
8316 \r
8317 LRESULT CALLBACK\r
8318 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8319 {\r
8320   static QuestionParams *qp;\r
8321   char reply[MSG_SIZ];\r
8322   int len, err;\r
8323 \r
8324   switch (message) {\r
8325   case WM_INITDIALOG:\r
8326     qp = (QuestionParams *) lParam;\r
8327     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8328     Translate(hDlg, DLG_Question);\r
8329     SetWindowText(hDlg, qp->title);\r
8330     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8331     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8332     return FALSE;\r
8333 \r
8334   case WM_COMMAND:\r
8335     switch (LOWORD(wParam)) {\r
8336     case IDOK:\r
8337       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8338       if (*reply) strcat(reply, " ");\r
8339       len = strlen(reply);\r
8340       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8341       strcat(reply, "\n");\r
8342       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8343       EndDialog(hDlg, TRUE);\r
8344       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8345       return TRUE;\r
8346     case IDCANCEL:\r
8347       EndDialog(hDlg, FALSE);\r
8348       return TRUE;\r
8349     default:\r
8350       break;\r
8351     }\r
8352     break;\r
8353   }\r
8354   return FALSE;\r
8355 }\r
8356 \r
8357 VOID\r
8358 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8359 {\r
8360     QuestionParams qp;\r
8361     FARPROC lpProc;\r
8362     \r
8363     qp.title = title;\r
8364     qp.question = question;\r
8365     qp.replyPrefix = replyPrefix;\r
8366     qp.pr = pr;\r
8367     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8368     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8369       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8370     FreeProcInstance(lpProc);\r
8371 }\r
8372 \r
8373 /* [AS] Pick FRC position */\r
8374 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8375 {\r
8376     static int * lpIndexFRC;\r
8377     BOOL index_is_ok;\r
8378     char buf[16];\r
8379 \r
8380     switch( message )\r
8381     {\r
8382     case WM_INITDIALOG:\r
8383         lpIndexFRC = (int *) lParam;\r
8384 \r
8385         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8386         Translate(hDlg, DLG_NewGameFRC);\r
8387 \r
8388         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8389         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8390         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8391         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8392 \r
8393         break;\r
8394 \r
8395     case WM_COMMAND:\r
8396         switch( LOWORD(wParam) ) {\r
8397         case IDOK:\r
8398             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8399             EndDialog( hDlg, 0 );\r
8400             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8401             return TRUE;\r
8402         case IDCANCEL:\r
8403             EndDialog( hDlg, 1 );   \r
8404             return TRUE;\r
8405         case IDC_NFG_Edit:\r
8406             if( HIWORD(wParam) == EN_CHANGE ) {\r
8407                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8408 \r
8409                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8410             }\r
8411             return TRUE;\r
8412         case IDC_NFG_Random:\r
8413           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8414             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8415             return TRUE;\r
8416         }\r
8417 \r
8418         break;\r
8419     }\r
8420 \r
8421     return FALSE;\r
8422 }\r
8423 \r
8424 int NewGameFRC()\r
8425 {\r
8426     int result;\r
8427     int index = appData.defaultFrcPosition;\r
8428     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8429 \r
8430     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8431 \r
8432     if( result == 0 ) {\r
8433         appData.defaultFrcPosition = index;\r
8434     }\r
8435 \r
8436     return result;\r
8437 }\r
8438 \r
8439 /* [AS] Game list options. Refactored by HGM */\r
8440 \r
8441 HWND gameListOptionsDialog;\r
8442 \r
8443 // low-level front-end: clear text edit / list widget\r
8444 void\r
8445 GLT_ClearList()\r
8446 {\r
8447     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8448 }\r
8449 \r
8450 // low-level front-end: clear text edit / list widget\r
8451 void\r
8452 GLT_DeSelectList()\r
8453 {\r
8454     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8455 }\r
8456 \r
8457 // low-level front-end: append line to text edit / list widget\r
8458 void\r
8459 GLT_AddToList( char *name )\r
8460 {\r
8461     if( name != 0 ) {\r
8462             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8463     }\r
8464 }\r
8465 \r
8466 // low-level front-end: get line from text edit / list widget\r
8467 Boolean\r
8468 GLT_GetFromList( int index, char *name )\r
8469 {\r
8470     if( name != 0 ) {\r
8471             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8472                 return TRUE;\r
8473     }\r
8474     return FALSE;\r
8475 }\r
8476 \r
8477 void GLT_MoveSelection( HWND hDlg, int delta )\r
8478 {\r
8479     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8480     int idx2 = idx1 + delta;\r
8481     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8482 \r
8483     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8484         char buf[128];\r
8485 \r
8486         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8487         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8488         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8489         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8490     }\r
8491 }\r
8492 \r
8493 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8494 {\r
8495     switch( message )\r
8496     {\r
8497     case WM_INITDIALOG:\r
8498         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8499         \r
8500         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8501         Translate(hDlg, DLG_GameListOptions);\r
8502 \r
8503         /* Initialize list */\r
8504         GLT_TagsToList( lpUserGLT );\r
8505 \r
8506         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8507 \r
8508         break;\r
8509 \r
8510     case WM_COMMAND:\r
8511         switch( LOWORD(wParam) ) {\r
8512         case IDOK:\r
8513             GLT_ParseList();\r
8514             EndDialog( hDlg, 0 );\r
8515             return TRUE;\r
8516         case IDCANCEL:\r
8517             EndDialog( hDlg, 1 );\r
8518             return TRUE;\r
8519 \r
8520         case IDC_GLT_Default:\r
8521             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8522             return TRUE;\r
8523 \r
8524         case IDC_GLT_Restore:\r
8525             GLT_TagsToList( appData.gameListTags );\r
8526             return TRUE;\r
8527 \r
8528         case IDC_GLT_Up:\r
8529             GLT_MoveSelection( hDlg, -1 );\r
8530             return TRUE;\r
8531 \r
8532         case IDC_GLT_Down:\r
8533             GLT_MoveSelection( hDlg, +1 );\r
8534             return TRUE;\r
8535         }\r
8536 \r
8537         break;\r
8538     }\r
8539 \r
8540     return FALSE;\r
8541 }\r
8542 \r
8543 int GameListOptions()\r
8544 {\r
8545     int result;\r
8546     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8547 \r
8548       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8549 \r
8550     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8551 \r
8552     if( result == 0 ) {\r
8553         /* [AS] Memory leak here! */\r
8554         appData.gameListTags = strdup( lpUserGLT ); \r
8555     }\r
8556 \r
8557     return result;\r
8558 }\r
8559 \r
8560 VOID\r
8561 DisplayIcsInteractionTitle(char *str)\r
8562 {\r
8563   char consoleTitle[MSG_SIZ];\r
8564 \r
8565     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8566     SetWindowText(hwndConsole, consoleTitle);\r
8567 \r
8568     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8569       char buf[MSG_SIZ], *p = buf, *q;\r
8570         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8571       do {\r
8572         q = strchr(p, ';');\r
8573         if(q) *q++ = 0;\r
8574         if(*p) ChatPopUp(p);\r
8575       } while(p=q);\r
8576     }\r
8577 \r
8578     SetActiveWindow(hwndMain);\r
8579 }\r
8580 \r
8581 void\r
8582 DrawPosition(int fullRedraw, Board board)\r
8583 {\r
8584   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8585 }\r
8586 \r
8587 void NotifyFrontendLogin()\r
8588 {\r
8589         if (hwndConsole)\r
8590                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8591 }\r
8592 \r
8593 VOID\r
8594 ResetFrontEnd()\r
8595 {\r
8596   fromX = fromY = -1;\r
8597   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8598     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8599     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8600     dragInfo.lastpos = dragInfo.pos;\r
8601     dragInfo.start.x = dragInfo.start.y = -1;\r
8602     dragInfo.from = dragInfo.start;\r
8603     ReleaseCapture();\r
8604     DrawPosition(TRUE, NULL);\r
8605   }\r
8606   TagsPopDown();\r
8607 }\r
8608 \r
8609 \r
8610 VOID\r
8611 CommentPopUp(char *title, char *str)\r
8612 {\r
8613   HWND hwnd = GetActiveWindow();\r
8614   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8615   SAY(str);\r
8616   SetActiveWindow(hwnd);\r
8617 }\r
8618 \r
8619 VOID\r
8620 CommentPopDown(void)\r
8621 {\r
8622   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8623   if (commentDialog) {\r
8624     ShowWindow(commentDialog, SW_HIDE);\r
8625   }\r
8626   commentUp = FALSE;\r
8627 }\r
8628 \r
8629 VOID\r
8630 EditCommentPopUp(int index, char *title, char *str)\r
8631 {\r
8632   EitherCommentPopUp(index, title, str, TRUE);\r
8633 }\r
8634 \r
8635 \r
8636 VOID\r
8637 RingBell()\r
8638 {\r
8639   MyPlaySound(&sounds[(int)SoundMove]);\r
8640 }\r
8641 \r
8642 VOID PlayIcsWinSound()\r
8643 {\r
8644   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8645 }\r
8646 \r
8647 VOID PlayIcsLossSound()\r
8648 {\r
8649   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8650 }\r
8651 \r
8652 VOID PlayIcsDrawSound()\r
8653 {\r
8654   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8655 }\r
8656 \r
8657 VOID PlayIcsUnfinishedSound()\r
8658 {\r
8659   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8660 }\r
8661 \r
8662 VOID\r
8663 PlayAlarmSound()\r
8664 {\r
8665   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8666 }\r
8667 \r
8668 VOID\r
8669 PlayTellSound()\r
8670 {\r
8671   MyPlaySound(&textAttribs[ColorTell].sound);\r
8672 }\r
8673 \r
8674 \r
8675 VOID\r
8676 EchoOn()\r
8677 {\r
8678   HWND hInput;\r
8679   consoleEcho = TRUE;\r
8680   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8681   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8682   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8683 }\r
8684 \r
8685 \r
8686 VOID\r
8687 EchoOff()\r
8688 {\r
8689   CHARFORMAT cf;\r
8690   HWND hInput;\r
8691   consoleEcho = FALSE;\r
8692   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8693   /* This works OK: set text and background both to the same color */\r
8694   cf = consoleCF;\r
8695   cf.crTextColor = COLOR_ECHOOFF;\r
8696   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8697   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8698 }\r
8699 \r
8700 /* No Raw()...? */\r
8701 \r
8702 void Colorize(ColorClass cc, int continuation)\r
8703 {\r
8704   currentColorClass = cc;\r
8705   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8706   consoleCF.crTextColor = textAttribs[cc].color;\r
8707   consoleCF.dwEffects = textAttribs[cc].effects;\r
8708   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8709 }\r
8710 \r
8711 char *\r
8712 UserName()\r
8713 {\r
8714   static char buf[MSG_SIZ];\r
8715   DWORD bufsiz = MSG_SIZ;\r
8716 \r
8717   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8718         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8719   }\r
8720   if (!GetUserName(buf, &bufsiz)) {\r
8721     /*DisplayError("Error getting user name", GetLastError());*/\r
8722     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8723   }\r
8724   return buf;\r
8725 }\r
8726 \r
8727 char *\r
8728 HostName()\r
8729 {\r
8730   static char buf[MSG_SIZ];\r
8731   DWORD bufsiz = MSG_SIZ;\r
8732 \r
8733   if (!GetComputerName(buf, &bufsiz)) {\r
8734     /*DisplayError("Error getting host name", GetLastError());*/\r
8735     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8736   }\r
8737   return buf;\r
8738 }\r
8739 \r
8740 \r
8741 int\r
8742 ClockTimerRunning()\r
8743 {\r
8744   return clockTimerEvent != 0;\r
8745 }\r
8746 \r
8747 int\r
8748 StopClockTimer()\r
8749 {\r
8750   if (clockTimerEvent == 0) return FALSE;\r
8751   KillTimer(hwndMain, clockTimerEvent);\r
8752   clockTimerEvent = 0;\r
8753   return TRUE;\r
8754 }\r
8755 \r
8756 void\r
8757 StartClockTimer(long millisec)\r
8758 {\r
8759   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8760                              (UINT) millisec, NULL);\r
8761 }\r
8762 \r
8763 void\r
8764 DisplayWhiteClock(long timeRemaining, int highlight)\r
8765 {\r
8766   HDC hdc;\r
8767   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8768 \r
8769   if(appData.noGUI) return;\r
8770   hdc = GetDC(hwndMain);\r
8771   if (!IsIconic(hwndMain)) {\r
8772     DisplayAClock(hdc, timeRemaining, highlight, \r
8773                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8774   }\r
8775   if (highlight && iconCurrent == iconBlack) {\r
8776     iconCurrent = iconWhite;\r
8777     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8778     if (IsIconic(hwndMain)) {\r
8779       DrawIcon(hdc, 2, 2, iconCurrent);\r
8780     }\r
8781   }\r
8782   (void) ReleaseDC(hwndMain, hdc);\r
8783   if (hwndConsole)\r
8784     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8785 }\r
8786 \r
8787 void\r
8788 DisplayBlackClock(long timeRemaining, int highlight)\r
8789 {\r
8790   HDC hdc;\r
8791   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8792 \r
8793   if(appData.noGUI) return;\r
8794   hdc = GetDC(hwndMain);\r
8795   if (!IsIconic(hwndMain)) {\r
8796     DisplayAClock(hdc, timeRemaining, highlight, \r
8797                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8798   }\r
8799   if (highlight && iconCurrent == iconWhite) {\r
8800     iconCurrent = iconBlack;\r
8801     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8802     if (IsIconic(hwndMain)) {\r
8803       DrawIcon(hdc, 2, 2, iconCurrent);\r
8804     }\r
8805   }\r
8806   (void) ReleaseDC(hwndMain, hdc);\r
8807   if (hwndConsole)\r
8808     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8809 }\r
8810 \r
8811 \r
8812 int\r
8813 LoadGameTimerRunning()\r
8814 {\r
8815   return loadGameTimerEvent != 0;\r
8816 }\r
8817 \r
8818 int\r
8819 StopLoadGameTimer()\r
8820 {\r
8821   if (loadGameTimerEvent == 0) return FALSE;\r
8822   KillTimer(hwndMain, loadGameTimerEvent);\r
8823   loadGameTimerEvent = 0;\r
8824   return TRUE;\r
8825 }\r
8826 \r
8827 void\r
8828 StartLoadGameTimer(long millisec)\r
8829 {\r
8830   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8831                                 (UINT) millisec, NULL);\r
8832 }\r
8833 \r
8834 void\r
8835 AutoSaveGame()\r
8836 {\r
8837   char *defName;\r
8838   FILE *f;\r
8839   char fileTitle[MSG_SIZ];\r
8840 \r
8841   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8842   f = OpenFileDialog(hwndMain, "a", defName,\r
8843                      appData.oldSaveStyle ? "gam" : "pgn",\r
8844                      GAME_FILT, \r
8845                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8846   if (f != NULL) {\r
8847     SaveGame(f, 0, "");\r
8848     fclose(f);\r
8849   }\r
8850 }\r
8851 \r
8852 \r
8853 void\r
8854 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8855 {\r
8856   if (delayedTimerEvent != 0) {\r
8857     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8858       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8859     }\r
8860     KillTimer(hwndMain, delayedTimerEvent);\r
8861     delayedTimerEvent = 0;\r
8862     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8863     delayedTimerCallback();\r
8864   }\r
8865   delayedTimerCallback = cb;\r
8866   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8867                                 (UINT) millisec, NULL);\r
8868 }\r
8869 \r
8870 DelayedEventCallback\r
8871 GetDelayedEvent()\r
8872 {\r
8873   if (delayedTimerEvent) {\r
8874     return delayedTimerCallback;\r
8875   } else {\r
8876     return NULL;\r
8877   }\r
8878 }\r
8879 \r
8880 void\r
8881 CancelDelayedEvent()\r
8882 {\r
8883   if (delayedTimerEvent) {\r
8884     KillTimer(hwndMain, delayedTimerEvent);\r
8885     delayedTimerEvent = 0;\r
8886   }\r
8887 }\r
8888 \r
8889 DWORD GetWin32Priority(int nice)\r
8890 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8891 /*\r
8892 REALTIME_PRIORITY_CLASS     0x00000100\r
8893 HIGH_PRIORITY_CLASS         0x00000080\r
8894 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8895 NORMAL_PRIORITY_CLASS       0x00000020\r
8896 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8897 IDLE_PRIORITY_CLASS         0x00000040\r
8898 */\r
8899         if (nice < -15) return 0x00000080;\r
8900         if (nice < 0)   return 0x00008000;\r
8901         if (nice == 0)  return 0x00000020;\r
8902         if (nice < 15)  return 0x00004000;\r
8903         return 0x00000040;\r
8904 }\r
8905 \r
8906 void RunCommand(char *cmdLine)\r
8907 {\r
8908   /* Now create the child process. */\r
8909   STARTUPINFO siStartInfo;\r
8910   PROCESS_INFORMATION piProcInfo;\r
8911 \r
8912   siStartInfo.cb = sizeof(STARTUPINFO);\r
8913   siStartInfo.lpReserved = NULL;\r
8914   siStartInfo.lpDesktop = NULL;\r
8915   siStartInfo.lpTitle = NULL;\r
8916   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8917   siStartInfo.cbReserved2 = 0;\r
8918   siStartInfo.lpReserved2 = NULL;\r
8919   siStartInfo.hStdInput = NULL;\r
8920   siStartInfo.hStdOutput = NULL;\r
8921   siStartInfo.hStdError = NULL;\r
8922 \r
8923   CreateProcess(NULL,\r
8924                 cmdLine,           /* command line */\r
8925                 NULL,      /* process security attributes */\r
8926                 NULL,      /* primary thread security attrs */\r
8927                 TRUE,      /* handles are inherited */\r
8928                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8929                 NULL,      /* use parent's environment */\r
8930                 NULL,\r
8931                 &siStartInfo, /* STARTUPINFO pointer */\r
8932                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8933 \r
8934   CloseHandle(piProcInfo.hThread);\r
8935 }\r
8936 \r
8937 /* Start a child process running the given program.\r
8938    The process's standard output can be read from "from", and its\r
8939    standard input can be written to "to".\r
8940    Exit with fatal error if anything goes wrong.\r
8941    Returns an opaque pointer that can be used to destroy the process\r
8942    later.\r
8943 */\r
8944 int\r
8945 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8946 {\r
8947 #define BUFSIZE 4096\r
8948 \r
8949   HANDLE hChildStdinRd, hChildStdinWr,\r
8950     hChildStdoutRd, hChildStdoutWr;\r
8951   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8952   SECURITY_ATTRIBUTES saAttr;\r
8953   BOOL fSuccess;\r
8954   PROCESS_INFORMATION piProcInfo;\r
8955   STARTUPINFO siStartInfo;\r
8956   ChildProc *cp;\r
8957   char buf[MSG_SIZ];\r
8958   DWORD err;\r
8959 \r
8960   if (appData.debugMode) {\r
8961     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8962   }\r
8963 \r
8964   *pr = NoProc;\r
8965 \r
8966   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8967   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8968   saAttr.bInheritHandle = TRUE;\r
8969   saAttr.lpSecurityDescriptor = NULL;\r
8970 \r
8971   /*\r
8972    * The steps for redirecting child's STDOUT:\r
8973    *     1. Create anonymous pipe to be STDOUT for child.\r
8974    *     2. Create a noninheritable duplicate of read handle,\r
8975    *         and close the inheritable read handle.\r
8976    */\r
8977 \r
8978   /* Create a pipe for the child's STDOUT. */\r
8979   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8980     return GetLastError();\r
8981   }\r
8982 \r
8983   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8984   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8985                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8986                              FALSE,     /* not inherited */\r
8987                              DUPLICATE_SAME_ACCESS);\r
8988   if (! fSuccess) {\r
8989     return GetLastError();\r
8990   }\r
8991   CloseHandle(hChildStdoutRd);\r
8992 \r
8993   /*\r
8994    * The steps for redirecting child's STDIN:\r
8995    *     1. Create anonymous pipe to be STDIN for child.\r
8996    *     2. Create a noninheritable duplicate of write handle,\r
8997    *         and close the inheritable write handle.\r
8998    */\r
8999 \r
9000   /* Create a pipe for the child's STDIN. */\r
9001   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9002     return GetLastError();\r
9003   }\r
9004 \r
9005   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9006   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9007                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9008                              FALSE,     /* not inherited */\r
9009                              DUPLICATE_SAME_ACCESS);\r
9010   if (! fSuccess) {\r
9011     return GetLastError();\r
9012   }\r
9013   CloseHandle(hChildStdinWr);\r
9014 \r
9015   /* Arrange to (1) look in dir for the child .exe file, and\r
9016    * (2) have dir be the child's working directory.  Interpret\r
9017    * dir relative to the directory WinBoard loaded from. */\r
9018   GetCurrentDirectory(MSG_SIZ, buf);\r
9019   SetCurrentDirectory(installDir);\r
9020   SetCurrentDirectory(dir);\r
9021 \r
9022   /* Now create the child process. */\r
9023 \r
9024   siStartInfo.cb = sizeof(STARTUPINFO);\r
9025   siStartInfo.lpReserved = NULL;\r
9026   siStartInfo.lpDesktop = NULL;\r
9027   siStartInfo.lpTitle = NULL;\r
9028   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9029   siStartInfo.cbReserved2 = 0;\r
9030   siStartInfo.lpReserved2 = NULL;\r
9031   siStartInfo.hStdInput = hChildStdinRd;\r
9032   siStartInfo.hStdOutput = hChildStdoutWr;\r
9033   siStartInfo.hStdError = hChildStdoutWr;\r
9034 \r
9035   fSuccess = CreateProcess(NULL,\r
9036                            cmdLine,        /* command line */\r
9037                            NULL,           /* process security attributes */\r
9038                            NULL,           /* primary thread security attrs */\r
9039                            TRUE,           /* handles are inherited */\r
9040                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9041                            NULL,           /* use parent's environment */\r
9042                            NULL,\r
9043                            &siStartInfo, /* STARTUPINFO pointer */\r
9044                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9045 \r
9046   err = GetLastError();\r
9047   SetCurrentDirectory(buf); /* return to prev directory */\r
9048   if (! fSuccess) {\r
9049     return err;\r
9050   }\r
9051 \r
9052   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9053     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9054     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9055   }\r
9056 \r
9057   /* Close the handles we don't need in the parent */\r
9058   CloseHandle(piProcInfo.hThread);\r
9059   CloseHandle(hChildStdinRd);\r
9060   CloseHandle(hChildStdoutWr);\r
9061 \r
9062   /* Prepare return value */\r
9063   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9064   cp->kind = CPReal;\r
9065   cp->hProcess = piProcInfo.hProcess;\r
9066   cp->pid = piProcInfo.dwProcessId;\r
9067   cp->hFrom = hChildStdoutRdDup;\r
9068   cp->hTo = hChildStdinWrDup;\r
9069 \r
9070   *pr = (void *) cp;\r
9071 \r
9072   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9073      2000 where engines sometimes don't see the initial command(s)\r
9074      from WinBoard and hang.  I don't understand how that can happen,\r
9075      but the Sleep is harmless, so I've put it in.  Others have also\r
9076      reported what may be the same problem, so hopefully this will fix\r
9077      it for them too.  */\r
9078   Sleep(500);\r
9079 \r
9080   return NO_ERROR;\r
9081 }\r
9082 \r
9083 \r
9084 void\r
9085 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9086 {\r
9087   ChildProc *cp; int result;\r
9088 \r
9089   cp = (ChildProc *) pr;\r
9090   if (cp == NULL) return;\r
9091 \r
9092   switch (cp->kind) {\r
9093   case CPReal:\r
9094     /* TerminateProcess is considered harmful, so... */\r
9095     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9096     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9097     /* The following doesn't work because the chess program\r
9098        doesn't "have the same console" as WinBoard.  Maybe\r
9099        we could arrange for this even though neither WinBoard\r
9100        nor the chess program uses a console for stdio? */\r
9101     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9102 \r
9103     /* [AS] Special termination modes for misbehaving programs... */\r
9104     if( signal == 9 ) { \r
9105         result = TerminateProcess( cp->hProcess, 0 );\r
9106 \r
9107         if ( appData.debugMode) {\r
9108             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9109         }\r
9110     }\r
9111     else if( signal == 10 ) {\r
9112         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9113 \r
9114         if( dw != WAIT_OBJECT_0 ) {\r
9115             result = TerminateProcess( cp->hProcess, 0 );\r
9116 \r
9117             if ( appData.debugMode) {\r
9118                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9119             }\r
9120 \r
9121         }\r
9122     }\r
9123 \r
9124     CloseHandle(cp->hProcess);\r
9125     break;\r
9126 \r
9127   case CPComm:\r
9128     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9129     break;\r
9130 \r
9131   case CPSock:\r
9132     closesocket(cp->sock);\r
9133     WSACleanup();\r
9134     break;\r
9135 \r
9136   case CPRcmd:\r
9137     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9138     closesocket(cp->sock);\r
9139     closesocket(cp->sock2);\r
9140     WSACleanup();\r
9141     break;\r
9142   }\r
9143   free(cp);\r
9144 }\r
9145 \r
9146 void\r
9147 InterruptChildProcess(ProcRef pr)\r
9148 {\r
9149   ChildProc *cp;\r
9150 \r
9151   cp = (ChildProc *) pr;\r
9152   if (cp == NULL) return;\r
9153   switch (cp->kind) {\r
9154   case CPReal:\r
9155     /* The following doesn't work because the chess program\r
9156        doesn't "have the same console" as WinBoard.  Maybe\r
9157        we could arrange for this even though neither WinBoard\r
9158        nor the chess program uses a console for stdio */\r
9159     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9160     break;\r
9161 \r
9162   case CPComm:\r
9163   case CPSock:\r
9164     /* Can't interrupt */\r
9165     break;\r
9166 \r
9167   case CPRcmd:\r
9168     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9169     break;\r
9170   }\r
9171 }\r
9172 \r
9173 \r
9174 int\r
9175 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9176 {\r
9177   char cmdLine[MSG_SIZ];\r
9178 \r
9179   if (port[0] == NULLCHAR) {\r
9180     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9181   } else {\r
9182     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9183   }\r
9184   return StartChildProcess(cmdLine, "", pr);\r
9185 }\r
9186 \r
9187 \r
9188 /* Code to open TCP sockets */\r
9189 \r
9190 int\r
9191 OpenTCP(char *host, char *port, ProcRef *pr)\r
9192 {\r
9193   ChildProc *cp;\r
9194   int err;\r
9195   SOCKET s;\r
9196 \r
9197   struct sockaddr_in sa, mysa;\r
9198   struct hostent FAR *hp;\r
9199   unsigned short uport;\r
9200   WORD wVersionRequested;\r
9201   WSADATA wsaData;\r
9202 \r
9203   /* Initialize socket DLL */\r
9204   wVersionRequested = MAKEWORD(1, 1);\r
9205   err = WSAStartup(wVersionRequested, &wsaData);\r
9206   if (err != 0) return err;\r
9207 \r
9208   /* Make socket */\r
9209   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9210     err = WSAGetLastError();\r
9211     WSACleanup();\r
9212     return err;\r
9213   }\r
9214 \r
9215   /* Bind local address using (mostly) don't-care values.\r
9216    */\r
9217   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9218   mysa.sin_family = AF_INET;\r
9219   mysa.sin_addr.s_addr = INADDR_ANY;\r
9220   uport = (unsigned short) 0;\r
9221   mysa.sin_port = htons(uport);\r
9222   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9223       == SOCKET_ERROR) {\r
9224     err = WSAGetLastError();\r
9225     WSACleanup();\r
9226     return err;\r
9227   }\r
9228 \r
9229   /* Resolve remote host name */\r
9230   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9231   if (!(hp = gethostbyname(host))) {\r
9232     unsigned int b0, b1, b2, b3;\r
9233 \r
9234     err = WSAGetLastError();\r
9235 \r
9236     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9237       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9238       hp->h_addrtype = AF_INET;\r
9239       hp->h_length = 4;\r
9240       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9241       hp->h_addr_list[0] = (char *) malloc(4);\r
9242       hp->h_addr_list[0][0] = (char) b0;\r
9243       hp->h_addr_list[0][1] = (char) b1;\r
9244       hp->h_addr_list[0][2] = (char) b2;\r
9245       hp->h_addr_list[0][3] = (char) b3;\r
9246     } else {\r
9247       WSACleanup();\r
9248       return err;\r
9249     }\r
9250   }\r
9251   sa.sin_family = hp->h_addrtype;\r
9252   uport = (unsigned short) atoi(port);\r
9253   sa.sin_port = htons(uport);\r
9254   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9255 \r
9256   /* Make connection */\r
9257   if (connect(s, (struct sockaddr *) &sa,\r
9258               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9259     err = WSAGetLastError();\r
9260     WSACleanup();\r
9261     return err;\r
9262   }\r
9263 \r
9264   /* Prepare return value */\r
9265   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9266   cp->kind = CPSock;\r
9267   cp->sock = s;\r
9268   *pr = (ProcRef *) cp;\r
9269 \r
9270   return NO_ERROR;\r
9271 }\r
9272 \r
9273 int\r
9274 OpenCommPort(char *name, ProcRef *pr)\r
9275 {\r
9276   HANDLE h;\r
9277   COMMTIMEOUTS ct;\r
9278   ChildProc *cp;\r
9279   char fullname[MSG_SIZ];\r
9280 \r
9281   if (*name != '\\')\r
9282     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9283   else\r
9284     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9285 \r
9286   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9287                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9288   if (h == (HANDLE) -1) {\r
9289     return GetLastError();\r
9290   }\r
9291   hCommPort = h;\r
9292 \r
9293   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9294 \r
9295   /* Accumulate characters until a 100ms pause, then parse */\r
9296   ct.ReadIntervalTimeout = 100;\r
9297   ct.ReadTotalTimeoutMultiplier = 0;\r
9298   ct.ReadTotalTimeoutConstant = 0;\r
9299   ct.WriteTotalTimeoutMultiplier = 0;\r
9300   ct.WriteTotalTimeoutConstant = 0;\r
9301   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9302 \r
9303   /* Prepare return value */\r
9304   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9305   cp->kind = CPComm;\r
9306   cp->hFrom = h;\r
9307   cp->hTo = h;\r
9308   *pr = (ProcRef *) cp;\r
9309 \r
9310   return NO_ERROR;\r
9311 }\r
9312 \r
9313 int\r
9314 OpenLoopback(ProcRef *pr)\r
9315 {\r
9316   DisplayFatalError(_("Not implemented"), 0, 1);\r
9317   return NO_ERROR;\r
9318 }\r
9319 \r
9320 \r
9321 int\r
9322 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9323 {\r
9324   ChildProc *cp;\r
9325   int err;\r
9326   SOCKET s, s2, s3;\r
9327   struct sockaddr_in sa, mysa;\r
9328   struct hostent FAR *hp;\r
9329   unsigned short uport;\r
9330   WORD wVersionRequested;\r
9331   WSADATA wsaData;\r
9332   int fromPort;\r
9333   char stderrPortStr[MSG_SIZ];\r
9334 \r
9335   /* Initialize socket DLL */\r
9336   wVersionRequested = MAKEWORD(1, 1);\r
9337   err = WSAStartup(wVersionRequested, &wsaData);\r
9338   if (err != 0) return err;\r
9339 \r
9340   /* Resolve remote host name */\r
9341   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9342   if (!(hp = gethostbyname(host))) {\r
9343     unsigned int b0, b1, b2, b3;\r
9344 \r
9345     err = WSAGetLastError();\r
9346 \r
9347     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9348       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9349       hp->h_addrtype = AF_INET;\r
9350       hp->h_length = 4;\r
9351       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9352       hp->h_addr_list[0] = (char *) malloc(4);\r
9353       hp->h_addr_list[0][0] = (char) b0;\r
9354       hp->h_addr_list[0][1] = (char) b1;\r
9355       hp->h_addr_list[0][2] = (char) b2;\r
9356       hp->h_addr_list[0][3] = (char) b3;\r
9357     } else {\r
9358       WSACleanup();\r
9359       return err;\r
9360     }\r
9361   }\r
9362   sa.sin_family = hp->h_addrtype;\r
9363   uport = (unsigned short) 514;\r
9364   sa.sin_port = htons(uport);\r
9365   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9366 \r
9367   /* Bind local socket to unused "privileged" port address\r
9368    */\r
9369   s = INVALID_SOCKET;\r
9370   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9371   mysa.sin_family = AF_INET;\r
9372   mysa.sin_addr.s_addr = INADDR_ANY;\r
9373   for (fromPort = 1023;; fromPort--) {\r
9374     if (fromPort < 0) {\r
9375       WSACleanup();\r
9376       return WSAEADDRINUSE;\r
9377     }\r
9378     if (s == INVALID_SOCKET) {\r
9379       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9380         err = WSAGetLastError();\r
9381         WSACleanup();\r
9382         return err;\r
9383       }\r
9384     }\r
9385     uport = (unsigned short) fromPort;\r
9386     mysa.sin_port = htons(uport);\r
9387     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9388         == SOCKET_ERROR) {\r
9389       err = WSAGetLastError();\r
9390       if (err == WSAEADDRINUSE) continue;\r
9391       WSACleanup();\r
9392       return err;\r
9393     }\r
9394     if (connect(s, (struct sockaddr *) &sa,\r
9395       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9396       err = WSAGetLastError();\r
9397       if (err == WSAEADDRINUSE) {\r
9398         closesocket(s);\r
9399         s = -1;\r
9400         continue;\r
9401       }\r
9402       WSACleanup();\r
9403       return err;\r
9404     }\r
9405     break;\r
9406   }\r
9407 \r
9408   /* Bind stderr local socket to unused "privileged" port address\r
9409    */\r
9410   s2 = INVALID_SOCKET;\r
9411   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9412   mysa.sin_family = AF_INET;\r
9413   mysa.sin_addr.s_addr = INADDR_ANY;\r
9414   for (fromPort = 1023;; fromPort--) {\r
9415     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9416     if (fromPort < 0) {\r
9417       (void) closesocket(s);\r
9418       WSACleanup();\r
9419       return WSAEADDRINUSE;\r
9420     }\r
9421     if (s2 == INVALID_SOCKET) {\r
9422       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9423         err = WSAGetLastError();\r
9424         closesocket(s);\r
9425         WSACleanup();\r
9426         return err;\r
9427       }\r
9428     }\r
9429     uport = (unsigned short) fromPort;\r
9430     mysa.sin_port = htons(uport);\r
9431     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9432         == SOCKET_ERROR) {\r
9433       err = WSAGetLastError();\r
9434       if (err == WSAEADDRINUSE) continue;\r
9435       (void) closesocket(s);\r
9436       WSACleanup();\r
9437       return err;\r
9438     }\r
9439     if (listen(s2, 1) == SOCKET_ERROR) {\r
9440       err = WSAGetLastError();\r
9441       if (err == WSAEADDRINUSE) {\r
9442         closesocket(s2);\r
9443         s2 = INVALID_SOCKET;\r
9444         continue;\r
9445       }\r
9446       (void) closesocket(s);\r
9447       (void) closesocket(s2);\r
9448       WSACleanup();\r
9449       return err;\r
9450     }\r
9451     break;\r
9452   }\r
9453   prevStderrPort = fromPort; // remember port used\r
9454   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9455 \r
9456   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9457     err = WSAGetLastError();\r
9458     (void) closesocket(s);\r
9459     (void) closesocket(s2);\r
9460     WSACleanup();\r
9461     return err;\r
9462   }\r
9463 \r
9464   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9465     err = WSAGetLastError();\r
9466     (void) closesocket(s);\r
9467     (void) closesocket(s2);\r
9468     WSACleanup();\r
9469     return err;\r
9470   }\r
9471   if (*user == NULLCHAR) user = UserName();\r
9472   if (send(s, user, strlen(user) + 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   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9480     err = WSAGetLastError();\r
9481     (void) closesocket(s);\r
9482     (void) closesocket(s2);\r
9483     WSACleanup();\r
9484     return err;\r
9485   }\r
9486 \r
9487   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9488     err = WSAGetLastError();\r
9489     (void) closesocket(s);\r
9490     (void) closesocket(s2);\r
9491     WSACleanup();\r
9492     return err;\r
9493   }\r
9494   (void) closesocket(s2);  /* Stop listening */\r
9495 \r
9496   /* Prepare return value */\r
9497   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9498   cp->kind = CPRcmd;\r
9499   cp->sock = s;\r
9500   cp->sock2 = s3;\r
9501   *pr = (ProcRef *) cp;\r
9502 \r
9503   return NO_ERROR;\r
9504 }\r
9505 \r
9506 \r
9507 InputSourceRef\r
9508 AddInputSource(ProcRef pr, int lineByLine,\r
9509                InputCallback func, VOIDSTAR closure)\r
9510 {\r
9511   InputSource *is, *is2 = NULL;\r
9512   ChildProc *cp = (ChildProc *) pr;\r
9513 \r
9514   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9515   is->lineByLine = lineByLine;\r
9516   is->func = func;\r
9517   is->closure = closure;\r
9518   is->second = NULL;\r
9519   is->next = is->buf;\r
9520   if (pr == NoProc) {\r
9521     is->kind = CPReal;\r
9522     consoleInputSource = is;\r
9523   } else {\r
9524     is->kind = cp->kind;\r
9525     /* \r
9526         [AS] Try to avoid a race condition if the thread is given control too early:\r
9527         we create all threads suspended so that the is->hThread variable can be\r
9528         safely assigned, then let the threads start with ResumeThread.\r
9529     */\r
9530     switch (cp->kind) {\r
9531     case CPReal:\r
9532       is->hFile = cp->hFrom;\r
9533       cp->hFrom = NULL; /* now owned by InputThread */\r
9534       is->hThread =\r
9535         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9536                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9537       break;\r
9538 \r
9539     case CPComm:\r
9540       is->hFile = cp->hFrom;\r
9541       cp->hFrom = NULL; /* now owned by InputThread */\r
9542       is->hThread =\r
9543         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9544                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9545       break;\r
9546 \r
9547     case CPSock:\r
9548       is->sock = cp->sock;\r
9549       is->hThread =\r
9550         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9551                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9552       break;\r
9553 \r
9554     case CPRcmd:\r
9555       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9556       *is2 = *is;\r
9557       is->sock = cp->sock;\r
9558       is->second = is2;\r
9559       is2->sock = cp->sock2;\r
9560       is2->second = is2;\r
9561       is->hThread =\r
9562         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9563                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9564       is2->hThread =\r
9565         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9566                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9567       break;\r
9568     }\r
9569 \r
9570     if( is->hThread != NULL ) {\r
9571         ResumeThread( is->hThread );\r
9572     }\r
9573 \r
9574     if( is2 != NULL && is2->hThread != NULL ) {\r
9575         ResumeThread( is2->hThread );\r
9576     }\r
9577   }\r
9578 \r
9579   return (InputSourceRef) is;\r
9580 }\r
9581 \r
9582 void\r
9583 RemoveInputSource(InputSourceRef isr)\r
9584 {\r
9585   InputSource *is;\r
9586 \r
9587   is = (InputSource *) isr;\r
9588   is->hThread = NULL;  /* tell thread to stop */\r
9589   CloseHandle(is->hThread);\r
9590   if (is->second != NULL) {\r
9591     is->second->hThread = NULL;\r
9592     CloseHandle(is->second->hThread);\r
9593   }\r
9594 }\r
9595 \r
9596 int no_wrap(char *message, int count)\r
9597 {\r
9598     ConsoleOutput(message, count, FALSE);\r
9599     return count;\r
9600 }\r
9601 \r
9602 int\r
9603 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9604 {\r
9605   DWORD dOutCount;\r
9606   int outCount = SOCKET_ERROR;\r
9607   ChildProc *cp = (ChildProc *) pr;\r
9608   static OVERLAPPED ovl;\r
9609   static int line = 0;\r
9610 \r
9611   if (pr == NoProc)\r
9612   {\r
9613     if (appData.noJoin || !appData.useInternalWrap)\r
9614       return no_wrap(message, count);\r
9615     else\r
9616     {\r
9617       int width = get_term_width();\r
9618       int len = wrap(NULL, message, count, width, &line);\r
9619       char *msg = malloc(len);\r
9620       int dbgchk;\r
9621 \r
9622       if (!msg)\r
9623         return no_wrap(message, count);\r
9624       else\r
9625       {\r
9626         dbgchk = wrap(msg, message, count, width, &line);\r
9627         if (dbgchk != len && appData.debugMode)\r
9628             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9629         ConsoleOutput(msg, len, FALSE);\r
9630         free(msg);\r
9631         return len;\r
9632       }\r
9633     }\r
9634   }\r
9635 \r
9636   if (ovl.hEvent == NULL) {\r
9637     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9638   }\r
9639   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9640 \r
9641   switch (cp->kind) {\r
9642   case CPSock:\r
9643   case CPRcmd:\r
9644     outCount = send(cp->sock, message, count, 0);\r
9645     if (outCount == SOCKET_ERROR) {\r
9646       *outError = WSAGetLastError();\r
9647     } else {\r
9648       *outError = NO_ERROR;\r
9649     }\r
9650     break;\r
9651 \r
9652   case CPReal:\r
9653     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9654                   &dOutCount, NULL)) {\r
9655       *outError = NO_ERROR;\r
9656       outCount = (int) dOutCount;\r
9657     } else {\r
9658       *outError = GetLastError();\r
9659     }\r
9660     break;\r
9661 \r
9662   case CPComm:\r
9663     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9664                             &dOutCount, &ovl);\r
9665     if (*outError == NO_ERROR) {\r
9666       outCount = (int) dOutCount;\r
9667     }\r
9668     break;\r
9669   }\r
9670   return outCount;\r
9671 }\r
9672 \r
9673 void\r
9674 DoSleep(int n)\r
9675 {\r
9676     if(n != 0) Sleep(n);\r
9677 }\r
9678 \r
9679 int\r
9680 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9681                        long msdelay)\r
9682 {\r
9683   /* Ignore delay, not implemented for WinBoard */\r
9684   return OutputToProcess(pr, message, count, outError);\r
9685 }\r
9686 \r
9687 \r
9688 void\r
9689 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9690                         char *buf, int count, int error)\r
9691 {\r
9692   DisplayFatalError(_("Not implemented"), 0, 1);\r
9693 }\r
9694 \r
9695 /* see wgamelist.c for Game List functions */\r
9696 /* see wedittags.c for Edit Tags functions */\r
9697 \r
9698 \r
9699 VOID\r
9700 ICSInitScript()\r
9701 {\r
9702   FILE *f;\r
9703   char buf[MSG_SIZ];\r
9704   char *dummy;\r
9705 \r
9706   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9707     f = fopen(buf, "r");\r
9708     if (f != NULL) {\r
9709       ProcessICSInitScript(f);\r
9710       fclose(f);\r
9711     }\r
9712   }\r
9713 }\r
9714 \r
9715 \r
9716 VOID\r
9717 StartAnalysisClock()\r
9718 {\r
9719   if (analysisTimerEvent) return;\r
9720   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9721                                         (UINT) 2000, NULL);\r
9722 }\r
9723 \r
9724 VOID\r
9725 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9726 {\r
9727   highlightInfo.sq[0].x = fromX;\r
9728   highlightInfo.sq[0].y = fromY;\r
9729   highlightInfo.sq[1].x = toX;\r
9730   highlightInfo.sq[1].y = toY;\r
9731 }\r
9732 \r
9733 VOID\r
9734 ClearHighlights()\r
9735 {\r
9736   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9737     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9738 }\r
9739 \r
9740 VOID\r
9741 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9742 {\r
9743   premoveHighlightInfo.sq[0].x = fromX;\r
9744   premoveHighlightInfo.sq[0].y = fromY;\r
9745   premoveHighlightInfo.sq[1].x = toX;\r
9746   premoveHighlightInfo.sq[1].y = toY;\r
9747 }\r
9748 \r
9749 VOID\r
9750 ClearPremoveHighlights()\r
9751 {\r
9752   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9753     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9754 }\r
9755 \r
9756 VOID\r
9757 ShutDownFrontEnd()\r
9758 {\r
9759   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9760   DeleteClipboardTempFiles();\r
9761 }\r
9762 \r
9763 void\r
9764 BoardToTop()\r
9765 {\r
9766     if (IsIconic(hwndMain))\r
9767       ShowWindow(hwndMain, SW_RESTORE);\r
9768 \r
9769     SetActiveWindow(hwndMain);\r
9770 }\r
9771 \r
9772 /*\r
9773  * Prototypes for animation support routines\r
9774  */\r
9775 static void ScreenSquare(int column, int row, POINT * pt);\r
9776 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9777      POINT frames[], int * nFrames);\r
9778 \r
9779 \r
9780 #define kFactor 4\r
9781 \r
9782 void\r
9783 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9784 {       // [HGM] atomic: animate blast wave\r
9785         int i;\r
9786 \r
9787         explodeInfo.fromX = fromX;\r
9788         explodeInfo.fromY = fromY;\r
9789         explodeInfo.toX = toX;\r
9790         explodeInfo.toY = toY;\r
9791         for(i=1; i<4*kFactor; i++) {\r
9792             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9793             DrawPosition(FALSE, board);\r
9794             Sleep(appData.animSpeed);\r
9795         }\r
9796         explodeInfo.radius = 0;\r
9797         DrawPosition(TRUE, board);\r
9798 }\r
9799 \r
9800 void\r
9801 AnimateMove(board, fromX, fromY, toX, toY)\r
9802      Board board;\r
9803      int fromX;\r
9804      int fromY;\r
9805      int toX;\r
9806      int toY;\r
9807 {\r
9808   ChessSquare piece;\r
9809   POINT start, finish, mid;\r
9810   POINT frames[kFactor * 2 + 1];\r
9811   int nFrames, n;\r
9812 \r
9813   if (!appData.animate) return;\r
9814   if (doingSizing) return;\r
9815   if (fromY < 0 || fromX < 0) return;\r
9816   piece = board[fromY][fromX];\r
9817   if (piece >= EmptySquare) return;\r
9818 \r
9819   ScreenSquare(fromX, fromY, &start);\r
9820   ScreenSquare(toX, toY, &finish);\r
9821 \r
9822   /* All moves except knight jumps move in straight line */\r
9823   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9824     mid.x = start.x + (finish.x - start.x) / 2;\r
9825     mid.y = start.y + (finish.y - start.y) / 2;\r
9826   } else {\r
9827     /* Knight: make straight movement then diagonal */\r
9828     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9829        mid.x = start.x + (finish.x - start.x) / 2;\r
9830        mid.y = start.y;\r
9831      } else {\r
9832        mid.x = start.x;\r
9833        mid.y = start.y + (finish.y - start.y) / 2;\r
9834      }\r
9835   }\r
9836   \r
9837   /* Don't use as many frames for very short moves */\r
9838   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9839     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9840   else\r
9841     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9842 \r
9843   animInfo.from.x = fromX;\r
9844   animInfo.from.y = fromY;\r
9845   animInfo.to.x = toX;\r
9846   animInfo.to.y = toY;\r
9847   animInfo.lastpos = start;\r
9848   animInfo.piece = piece;\r
9849   for (n = 0; n < nFrames; n++) {\r
9850     animInfo.pos = frames[n];\r
9851     DrawPosition(FALSE, NULL);\r
9852     animInfo.lastpos = animInfo.pos;\r
9853     Sleep(appData.animSpeed);\r
9854   }\r
9855   animInfo.pos = finish;\r
9856   DrawPosition(FALSE, NULL);\r
9857   animInfo.piece = EmptySquare;\r
9858   Explode(board, fromX, fromY, toX, toY);\r
9859 }\r
9860 \r
9861 /*      Convert board position to corner of screen rect and color       */\r
9862 \r
9863 static void\r
9864 ScreenSquare(column, row, pt)\r
9865      int column; int row; POINT * pt;\r
9866 {\r
9867   if (flipView) {\r
9868     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9869     pt->y = lineGap + row * (squareSize + lineGap);\r
9870   } else {\r
9871     pt->x = lineGap + column * (squareSize + lineGap);\r
9872     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9873   }\r
9874 }\r
9875 \r
9876 /*      Generate a series of frame coords from start->mid->finish.\r
9877         The movement rate doubles until the half way point is\r
9878         reached, then halves back down to the final destination,\r
9879         which gives a nice slow in/out effect. The algorithmn\r
9880         may seem to generate too many intermediates for short\r
9881         moves, but remember that the purpose is to attract the\r
9882         viewers attention to the piece about to be moved and\r
9883         then to where it ends up. Too few frames would be less\r
9884         noticeable.                                             */\r
9885 \r
9886 static void\r
9887 Tween(start, mid, finish, factor, frames, nFrames)\r
9888      POINT * start; POINT * mid;\r
9889      POINT * finish; int factor;\r
9890      POINT frames[]; int * nFrames;\r
9891 {\r
9892   int n, fraction = 1, count = 0;\r
9893 \r
9894   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9895   for (n = 0; n < factor; n++)\r
9896     fraction *= 2;\r
9897   for (n = 0; n < factor; n++) {\r
9898     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9899     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9900     count ++;\r
9901     fraction = fraction / 2;\r
9902   }\r
9903   \r
9904   /* Midpoint */\r
9905   frames[count] = *mid;\r
9906   count ++;\r
9907   \r
9908   /* Slow out, stepping 1/2, then 1/4, ... */\r
9909   fraction = 2;\r
9910   for (n = 0; n < factor; n++) {\r
9911     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9912     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9913     count ++;\r
9914     fraction = fraction * 2;\r
9915   }\r
9916   *nFrames = count;\r
9917 }\r
9918 \r
9919 void\r
9920 SettingsPopUp(ChessProgramState *cps)\r
9921 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9922       EngineOptionsPopup(savedHwnd, cps);\r
9923 }\r
9924 \r
9925 int flock(int fid, int code)\r
9926 {\r
9927     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9928     OVERLAPPED ov;\r
9929     ov.hEvent = NULL;\r
9930     ov.Offset = 0;\r
9931     ov.OffsetHigh = 0;\r
9932     switch(code) {\r
9933       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9934       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9935       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9936       default: return -1;\r
9937     }\r
9938     return 0;\r
9939 }\r