Put recently used engines in WB menu
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][42] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
257   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
318   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
319   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
322   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
323 { DLG_MoveHistory }, \r
324 { DLG_EvalGraph }, \r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
328   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
329   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
330   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
332   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
333   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
334 { 0 }\r
335 };\r
336 \r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
338 static int lastChecked;\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
340 extern int tinyLayout;\r
341 extern char * menuBarText[][10];\r
342 \r
343 void\r
344 LoadLanguageFile(char *name)\r
345 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
346     FILE *f;\r
347     int i=0, j=0, n=0, k;\r
348     char buf[MSG_SIZ];\r
349 \r
350     if(!name || name[0] == NULLCHAR) return;\r
351       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
352     appData.language = oldLanguage;\r
353     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
354     if((f = fopen(buf, "r")) == NULL) return;\r
355     while((k = fgetc(f)) != EOF) {\r
356         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
357         languageBuf[i] = k;\r
358         if(k == '\n') {\r
359             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
360                 char *p;\r
361                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
362                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
363                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
364                         english[j] = languageBuf + n + 1; *p = 0;\r
365                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
367                     }\r
368                 }\r
369             }\r
370             n = i + 1;\r
371         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
372             switch(k) {\r
373               case 'n': k = '\n'; break;\r
374               case 'r': k = '\r'; break;\r
375               case 't': k = '\t'; break;\r
376             }\r
377             languageBuf[--i] = k;\r
378         }\r
379         i++;\r
380     }\r
381     fclose(f);\r
382     barbaric = (j != 0);\r
383     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
384 }\r
385 \r
386 char *\r
387 T_(char *s)\r
388 {   // return the translation of the given string\r
389     // efficiency can be improved a lot...\r
390     int i=0;\r
391     static char buf[MSG_SIZ];\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
393     if(!barbaric) return s;\r
394     if(!s) return ""; // sanity\r
395     while(english[i]) {\r
396         if(!strcmp(s, english[i])) return foreign[i];\r
397         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
398             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
399             return buf;\r
400         }\r
401         i++;\r
402     }\r
403     return s;\r
404 }\r
405 \r
406 void\r
407 Translate(HWND hDlg, int dialogID)\r
408 {   // translate all text items in the given dialog\r
409     int i=0, j, k;\r
410     char buf[MSG_SIZ], *s;\r
411     if(!barbaric) return;\r
412     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
413     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
414     GetWindowText( hDlg, buf, MSG_SIZ );\r
415     s = T_(buf);\r
416     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
417     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
418         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
419         if(strlen(buf) == 0) continue;\r
420         s = T_(buf);\r
421         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
422     }\r
423 }\r
424 \r
425 HMENU\r
426 TranslateOneMenu(int i, HMENU subMenu)\r
427 {\r
428     int j;\r
429     static MENUITEMINFO info;\r
430 \r
431     info.cbSize = sizeof(MENUITEMINFO);\r
432     info.fMask = MIIM_STATE | MIIM_TYPE;\r
433           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
434             char buf[MSG_SIZ];\r
435             info.dwTypeData = buf;\r
436             info.cch = sizeof(buf);\r
437             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
438             if(i < 10) {\r
439                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
440                 else menuText[i][j] = strdup(buf); // remember original on first change\r
441             }\r
442             if(buf[0] == NULLCHAR) continue;\r
443             info.dwTypeData = T_(buf);\r
444             info.cch = strlen(buf)+1;\r
445             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
446           }\r
447     return subMenu;\r
448 }\r
449 \r
450 void\r
451 TranslateMenus(int addLanguage)\r
452 {\r
453     int i;\r
454     WIN32_FIND_DATA fileData;\r
455     HANDLE hFind;\r
456 #define IDM_English 1970\r
457     if(1) {\r
458         HMENU mainMenu = GetMenu(hwndMain);\r
459         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
460           HMENU subMenu = GetSubMenu(mainMenu, i);\r
461           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
462                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
463           TranslateOneMenu(i, subMenu);\r
464         }\r
465         DrawMenuBar(hwndMain);\r
466     }\r
467 \r
468     if(!addLanguage) return;\r
469     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
470         HMENU mainMenu = GetMenu(hwndMain);\r
471         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
472         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
473         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
474         i = 0; lastChecked = IDM_English;\r
475         do {\r
476             char *p, *q = fileData.cFileName;\r
477             int checkFlag = MF_UNCHECKED;\r
478             languageFile[i] = strdup(q);\r
479             if(barbaric && !strcmp(oldLanguage, q)) {\r
480                 checkFlag = MF_CHECKED;\r
481                 lastChecked = IDM_English + i + 1;\r
482                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
483             }\r
484             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
485             p = strstr(fileData.cFileName, ".lng");\r
486             if(p) *p = 0;\r
487             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
488         } while(FindNextFile(hFind, &fileData));\r
489         FindClose(hFind);\r
490     }\r
491 }\r
492 \r
493 #endif\r
494 \r
495 #define IDM_RecentEngines 3000\r
496 \r
497 void\r
498 RecentEngineMenu (char *s)\r
499 {\r
500     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
501         HMENU mainMenu = GetMenu(hwndMain);\r
502         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
503         int i=IDM_RecentEngines;\r
504         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
505         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
506         while(*s) {\r
507           char *p = strchr(s, '\n');\r
508           if(p == NULL) return; // malformed!\r
509           *p = NULLCHAR;\r
510           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
511           *p = '\n';\r
512           s = p+1;\r
513         }\r
514     }\r
515 }\r
516 \r
517 \r
518 typedef struct {\r
519   char *name;\r
520   int squareSize;\r
521   int lineGap;\r
522   int smallLayout;\r
523   int tinyLayout;\r
524   int cliWidth, cliHeight;\r
525 } SizeInfo;\r
526 \r
527 SizeInfo sizeInfo[] = \r
528 {\r
529   { "tiny",     21, 0, 1, 1, 0, 0 },\r
530   { "teeny",    25, 1, 1, 1, 0, 0 },\r
531   { "dinky",    29, 1, 1, 1, 0, 0 },\r
532   { "petite",   33, 1, 1, 1, 0, 0 },\r
533   { "slim",     37, 2, 1, 0, 0, 0 },\r
534   { "small",    40, 2, 1, 0, 0, 0 },\r
535   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
536   { "middling", 49, 2, 0, 0, 0, 0 },\r
537   { "average",  54, 2, 0, 0, 0, 0 },\r
538   { "moderate", 58, 3, 0, 0, 0, 0 },\r
539   { "medium",   64, 3, 0, 0, 0, 0 },\r
540   { "bulky",    72, 3, 0, 0, 0, 0 },\r
541   { "large",    80, 3, 0, 0, 0, 0 },\r
542   { "big",      87, 3, 0, 0, 0, 0 },\r
543   { "huge",     95, 3, 0, 0, 0, 0 },\r
544   { "giant",    108, 3, 0, 0, 0, 0 },\r
545   { "colossal", 116, 4, 0, 0, 0, 0 },\r
546   { "titanic",  129, 4, 0, 0, 0, 0 },\r
547   { NULL, 0, 0, 0, 0, 0, 0 }\r
548 };\r
549 \r
550 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
551 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
552 {\r
553   { 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
554   { 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
555   { 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
556   { 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
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571 };\r
572 \r
573 MyFont *font[NUM_SIZES][NUM_FONTS];\r
574 \r
575 typedef struct {\r
576   char *label;\r
577   int id;\r
578   HWND hwnd;\r
579   WNDPROC wndproc;\r
580 } MyButtonDesc;\r
581 \r
582 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
583 #define N_BUTTONS 5\r
584 \r
585 MyButtonDesc buttonDesc[N_BUTTONS] =\r
586 {\r
587   {"<<", IDM_ToStart, NULL, NULL},\r
588   {"<", IDM_Backward, NULL, NULL},\r
589   {"P", IDM_Pause, NULL, NULL},\r
590   {">", IDM_Forward, NULL, NULL},\r
591   {">>", IDM_ToEnd, NULL, NULL},\r
592 };\r
593 \r
594 int tinyLayout = 0, smallLayout = 0;\r
595 #define MENU_BAR_ITEMS 9\r
596 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
597   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
598   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
599 };\r
600 \r
601 \r
602 MySound sounds[(int)NSoundClasses];\r
603 MyTextAttribs textAttribs[(int)NColorClasses];\r
604 \r
605 MyColorizeAttribs colorizeAttribs[] = {\r
606   { (COLORREF)0, 0, N_("Shout Text") },\r
607   { (COLORREF)0, 0, N_("SShout/CShout") },\r
608   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
609   { (COLORREF)0, 0, N_("Channel Text") },\r
610   { (COLORREF)0, 0, N_("Kibitz Text") },\r
611   { (COLORREF)0, 0, N_("Tell Text") },\r
612   { (COLORREF)0, 0, N_("Challenge Text") },\r
613   { (COLORREF)0, 0, N_("Request Text") },\r
614   { (COLORREF)0, 0, N_("Seek Text") },\r
615   { (COLORREF)0, 0, N_("Normal Text") },\r
616   { (COLORREF)0, 0, N_("None") }\r
617 };\r
618 \r
619 \r
620 \r
621 static char *commentTitle;\r
622 static char *commentText;\r
623 static int commentIndex;\r
624 static Boolean editComment = FALSE;\r
625 \r
626 \r
627 char errorTitle[MSG_SIZ];\r
628 char errorMessage[2*MSG_SIZ];\r
629 HWND errorDialog = NULL;\r
630 BOOLEAN moveErrorMessageUp = FALSE;\r
631 BOOLEAN consoleEcho = TRUE;\r
632 CHARFORMAT consoleCF;\r
633 COLORREF consoleBackgroundColor;\r
634 \r
635 char *programVersion;\r
636 \r
637 #define CPReal 1\r
638 #define CPComm 2\r
639 #define CPSock 3\r
640 #define CPRcmd 4\r
641 typedef int CPKind;\r
642 \r
643 typedef struct {\r
644   CPKind kind;\r
645   HANDLE hProcess;\r
646   DWORD pid;\r
647   HANDLE hTo;\r
648   HANDLE hFrom;\r
649   SOCKET sock;\r
650   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
651 } ChildProc;\r
652 \r
653 #define INPUT_SOURCE_BUF_SIZE 4096\r
654 \r
655 typedef struct _InputSource {\r
656   CPKind kind;\r
657   HANDLE hFile;\r
658   SOCKET sock;\r
659   int lineByLine;\r
660   HANDLE hThread;\r
661   DWORD id;\r
662   char buf[INPUT_SOURCE_BUF_SIZE];\r
663   char *next;\r
664   DWORD count;\r
665   int error;\r
666   InputCallback func;\r
667   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
668   VOIDSTAR closure;\r
669 } InputSource;\r
670 \r
671 InputSource *consoleInputSource;\r
672 \r
673 DCB dcb;\r
674 \r
675 /* forward */\r
676 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
677 VOID ConsoleCreate();\r
678 LRESULT CALLBACK\r
679   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
680 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
681 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
682 VOID ParseCommSettings(char *arg, DCB *dcb);\r
683 LRESULT CALLBACK\r
684   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
685 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
686 void ParseIcsTextMenu(char *icsTextMenuString);\r
687 VOID PopUpNameDialog(char firstchar);\r
688 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
689 \r
690 /* [AS] */\r
691 int NewGameFRC();\r
692 int GameListOptions();\r
693 \r
694 int dummy; // [HGM] for obsolete args\r
695 \r
696 HWND hwndMain = NULL;        /* root window*/\r
697 HWND hwndConsole = NULL;\r
698 HWND commentDialog = NULL;\r
699 HWND moveHistoryDialog = NULL;\r
700 HWND evalGraphDialog = NULL;\r
701 HWND engineOutputDialog = NULL;\r
702 HWND gameListDialog = NULL;\r
703 HWND editTagsDialog = NULL;\r
704 \r
705 int commentUp = FALSE;\r
706 \r
707 WindowPlacement wpMain;\r
708 WindowPlacement wpConsole;\r
709 WindowPlacement wpComment;\r
710 WindowPlacement wpMoveHistory;\r
711 WindowPlacement wpEvalGraph;\r
712 WindowPlacement wpEngineOutput;\r
713 WindowPlacement wpGameList;\r
714 WindowPlacement wpTags;\r
715 \r
716 VOID EngineOptionsPopup(); // [HGM] settings\r
717 \r
718 VOID GothicPopUp(char *title, VariantClass variant);\r
719 /*\r
720  * Setting "frozen" should disable all user input other than deleting\r
721  * the window.  We do this while engines are initializing themselves.\r
722  */\r
723 static int frozen = 0;\r
724 static int oldMenuItemState[MENU_BAR_ITEMS];\r
725 void FreezeUI()\r
726 {\r
727   HMENU hmenu;\r
728   int i;\r
729 \r
730   if (frozen) return;\r
731   frozen = 1;\r
732   hmenu = GetMenu(hwndMain);\r
733   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
734     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
735   }\r
736   DrawMenuBar(hwndMain);\r
737 }\r
738 \r
739 /* Undo a FreezeUI */\r
740 void ThawUI()\r
741 {\r
742   HMENU hmenu;\r
743   int i;\r
744 \r
745   if (!frozen) return;\r
746   frozen = 0;\r
747   hmenu = GetMenu(hwndMain);\r
748   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
749     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
750   }\r
751   DrawMenuBar(hwndMain);\r
752 }\r
753 \r
754 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
755 \r
756 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
757 #ifdef JAWS\r
758 #include "jaws.c"\r
759 #else\r
760 #define JAWS_INIT\r
761 #define JAWS_ARGS\r
762 #define JAWS_ALT_INTERCEPT\r
763 #define JAWS_KBUP_NAVIGATION\r
764 #define JAWS_KBDOWN_NAVIGATION\r
765 #define JAWS_MENU_ITEMS\r
766 #define JAWS_SILENCE\r
767 #define JAWS_REPLAY\r
768 #define JAWS_ACCEL\r
769 #define JAWS_COPYRIGHT\r
770 #define JAWS_DELETE(X) X\r
771 #define SAYMACHINEMOVE()\r
772 #define SAY(X)\r
773 #endif\r
774 \r
775 /*---------------------------------------------------------------------------*\\r
776  *\r
777  * WinMain\r
778  *\r
779 \*---------------------------------------------------------------------------*/\r
780 \r
781 int APIENTRY\r
782 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
783         LPSTR lpCmdLine, int nCmdShow)\r
784 {\r
785   MSG msg;\r
786   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
787 //  INITCOMMONCONTROLSEX ex;\r
788 \r
789   debugFP = stderr;\r
790 \r
791   LoadLibrary("RICHED32.DLL");\r
792   consoleCF.cbSize = sizeof(CHARFORMAT);\r
793 \r
794   if (!InitApplication(hInstance)) {\r
795     return (FALSE);\r
796   }\r
797   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
798     return (FALSE);\r
799   }\r
800 \r
801   JAWS_INIT\r
802   TranslateMenus(1);\r
803 \r
804 //  InitCommonControlsEx(&ex);\r
805   InitCommonControls();\r
806 \r
807   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
808   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
809   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
810 \r
811   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
812 \r
813   while (GetMessage(&msg, /* message structure */\r
814                     NULL, /* handle of window receiving the message */\r
815                     0,    /* lowest message to examine */\r
816                     0))   /* highest message to examine */\r
817     {\r
818 \r
819       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
820         // [HGM] navigate: switch between all windows with tab\r
821         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
822         int i, currentElement = 0;\r
823 \r
824         // first determine what element of the chain we come from (if any)\r
825         if(appData.icsActive) {\r
826             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
827             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
828         }\r
829         if(engineOutputDialog && EngineOutputIsUp()) {\r
830             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
831             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
832         }\r
833         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
834             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
835         }\r
836         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
837         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
838         if(msg.hwnd == e1)                 currentElement = 2; else\r
839         if(msg.hwnd == e2)                 currentElement = 3; else\r
840         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
841         if(msg.hwnd == mh)                currentElement = 4; else\r
842         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
843         if(msg.hwnd == hText)  currentElement = 5; else\r
844         if(msg.hwnd == hInput) currentElement = 6; else\r
845         for (i = 0; i < N_BUTTONS; i++) {\r
846             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
847         }\r
848 \r
849         // determine where to go to\r
850         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
851           do {\r
852             currentElement = (currentElement + direction) % 7;\r
853             switch(currentElement) {\r
854                 case 0:\r
855                   h = hwndMain; break; // passing this case always makes the loop exit\r
856                 case 1:\r
857                   h = buttonDesc[0].hwnd; break; // could be NULL\r
858                 case 2:\r
859                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
860                   h = e1; break;\r
861                 case 3:\r
862                   if(!EngineOutputIsUp()) continue;\r
863                   h = e2; break;\r
864                 case 4:\r
865                   if(!MoveHistoryIsUp()) continue;\r
866                   h = mh; break;\r
867 //              case 6: // input to eval graph does not seem to get here!\r
868 //                if(!EvalGraphIsUp()) continue;\r
869 //                h = evalGraphDialog; break;\r
870                 case 5:\r
871                   if(!appData.icsActive) continue;\r
872                   SAY("display");\r
873                   h = hText; break;\r
874                 case 6:\r
875                   if(!appData.icsActive) continue;\r
876                   SAY("input");\r
877                   h = hInput; break;\r
878             }\r
879           } while(h == 0);\r
880 \r
881           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
882           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
883           SetFocus(h);\r
884 \r
885           continue; // this message now has been processed\r
886         }\r
887       }\r
888 \r
889       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
890           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
891           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
892           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
893           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
894           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
895           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
896           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
897           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
898           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
899         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
900         for(i=0; i<MAX_CHAT; i++) \r
901             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
902                 done = 1; break;\r
903         }\r
904         if(done) continue; // [HGM] chat: end patch\r
905         TranslateMessage(&msg); /* Translates virtual key codes */\r
906         DispatchMessage(&msg);  /* Dispatches message to window */\r
907       }\r
908     }\r
909 \r
910 \r
911   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
912 }\r
913 \r
914 /*---------------------------------------------------------------------------*\\r
915  *\r
916  * Initialization functions\r
917  *\r
918 \*---------------------------------------------------------------------------*/\r
919 \r
920 void\r
921 SetUserLogo()\r
922 {   // update user logo if necessary\r
923     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
924 \r
925     if(appData.autoLogo) {\r
926           curName = UserName();\r
927           if(strcmp(curName, oldUserName)) {\r
928                 GetCurrentDirectory(MSG_SIZ, dir);\r
929                 SetCurrentDirectory(installDir);\r
930                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
931                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
932                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
933                 if(userLogo == NULL)\r
934                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
935                 SetCurrentDirectory(dir); /* return to prev directory */\r
936           }\r
937     }\r
938 }\r
939 \r
940 BOOL\r
941 InitApplication(HINSTANCE hInstance)\r
942 {\r
943   WNDCLASS wc;\r
944 \r
945   /* Fill in window class structure with parameters that describe the */\r
946   /* main window. */\r
947 \r
948   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
949   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
950   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
951   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
952   wc.hInstance     = hInstance;         /* Owner of this class */\r
953   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
954   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
955   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
956   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
957   wc.lpszClassName = szAppName;                 /* Name to register as */\r
958 \r
959   /* Register the window class and return success/failure code. */\r
960   if (!RegisterClass(&wc)) return FALSE;\r
961 \r
962   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
963   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
964   wc.cbClsExtra    = 0;\r
965   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
966   wc.hInstance     = hInstance;\r
967   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
968   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
969   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
970   wc.lpszMenuName  = NULL;\r
971   wc.lpszClassName = szConsoleName;\r
972 \r
973   if (!RegisterClass(&wc)) return FALSE;\r
974   return TRUE;\r
975 }\r
976 \r
977 \r
978 /* Set by InitInstance, used by EnsureOnScreen */\r
979 int screenHeight, screenWidth;\r
980 \r
981 void\r
982 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
983 {\r
984 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
985   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
986   if (*x > screenWidth - 32) *x = 0;\r
987   if (*y > screenHeight - 32) *y = 0;\r
988   if (*x < minX) *x = minX;\r
989   if (*y < minY) *y = minY;\r
990 }\r
991 \r
992 VOID\r
993 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
994 {\r
995   char buf[MSG_SIZ], dir[MSG_SIZ];\r
996   GetCurrentDirectory(MSG_SIZ, dir);\r
997   SetCurrentDirectory(installDir);\r
998   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
999       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (cps->programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1006         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1007         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1008       } else\r
1009       if(appData.directory[n] && appData.directory[n][0]) {\r
1010         SetCurrentDirectory(appData.directory[n]);\r
1011         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1012       }\r
1013   }\r
1014   SetCurrentDirectory(dir); /* return to prev directory */\r
1015 }\r
1016 \r
1017 VOID\r
1018 InitTextures()\r
1019 {\r
1020   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1021   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1022   \r
1023   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1024       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1025       liteBackTextureMode = appData.liteBackTextureMode;\r
1026 \r
1027       if (liteBackTexture == NULL && appData.debugMode) {\r
1028           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1029       }\r
1030   }\r
1031   \r
1032   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1033       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1034       darkBackTextureMode = appData.darkBackTextureMode;\r
1035 \r
1036       if (darkBackTexture == NULL && appData.debugMode) {\r
1037           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1038       }\r
1039   }\r
1040 }\r
1041 \r
1042 BOOL\r
1043 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1044 {\r
1045   HWND hwnd; /* Main window handle. */\r
1046   int ibs;\r
1047   WINDOWPLACEMENT wp;\r
1048   char *filepart;\r
1049 \r
1050   hInst = hInstance;    /* Store instance handle in our global variable */\r
1051   programName = szAppName;\r
1052 \r
1053   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1054     *filepart = NULLCHAR;\r
1055   } else {\r
1056     GetCurrentDirectory(MSG_SIZ, installDir);\r
1057   }\r
1058   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1059   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1060   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1061   /* xboard, and older WinBoards, controlled the move sound with the\r
1062      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1063      always turn the option on (so that the backend will call us),\r
1064      then let the user turn the sound off by setting it to silence if\r
1065      desired.  To accommodate old winboard.ini files saved by old\r
1066      versions of WinBoard, we also turn off the sound if the option\r
1067      was initially set to false. [HGM] taken out of InitAppData */\r
1068   if (!appData.ringBellAfterMoves) {\r
1069     sounds[(int)SoundMove].name = strdup("");\r
1070     appData.ringBellAfterMoves = TRUE;\r
1071   }\r
1072   if (appData.debugMode) {\r
1073     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1074     setbuf(debugFP, NULL);\r
1075   }\r
1076 \r
1077   LoadLanguageFile(appData.language);\r
1078 \r
1079   InitBackEnd1();\r
1080 \r
1081 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1082 //  InitEngineUCI( installDir, &second );\r
1083 \r
1084   /* Create a main window for this application instance. */\r
1085   hwnd = CreateWindow(szAppName, szTitle,\r
1086                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1087                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1088                       NULL, NULL, hInstance, NULL);\r
1089   hwndMain = hwnd;\r
1090 \r
1091   /* If window could not be created, return "failure" */\r
1092   if (!hwnd) {\r
1093     return (FALSE);\r
1094   }\r
1095 \r
1096   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1097   LoadLogo(&first, 0, FALSE);\r
1098   LoadLogo(&second, 1, appData.icsActive);\r
1099 \r
1100   SetUserLogo();\r
1101 \r
1102   iconWhite = LoadIcon(hInstance, "icon_white");\r
1103   iconBlack = LoadIcon(hInstance, "icon_black");\r
1104   iconCurrent = iconWhite;\r
1105   InitDrawingColors();\r
1106   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1107   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1108   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1109     /* Compute window size for each board size, and use the largest\r
1110        size that fits on this screen as the default. */\r
1111     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1112     if (boardSize == (BoardSize)-1 &&\r
1113         winH <= screenHeight\r
1114            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1115         && winW <= screenWidth) {\r
1116       boardSize = (BoardSize)ibs;\r
1117     }\r
1118   }\r
1119 \r
1120   InitDrawingSizes(boardSize, 0);\r
1121   RecentEngineMenu(appData.recentEngineList);\r
1122   InitMenuChecks();\r
1123   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1124 \r
1125   /* [AS] Load textures if specified */\r
1126   InitTextures();\r
1127 \r
1128   mysrandom( (unsigned) time(NULL) );\r
1129 \r
1130   /* [AS] Restore layout */\r
1131   if( wpMoveHistory.visible ) {\r
1132       MoveHistoryPopUp();\r
1133   }\r
1134 \r
1135   if( wpEvalGraph.visible ) {\r
1136       EvalGraphPopUp();\r
1137   }\r
1138 \r
1139   if( wpEngineOutput.visible ) {\r
1140       EngineOutputPopUp();\r
1141   }\r
1142 \r
1143   /* Make the window visible; update its client area; and return "success" */\r
1144   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1145   wp.length = sizeof(WINDOWPLACEMENT);\r
1146   wp.flags = 0;\r
1147   wp.showCmd = nCmdShow;\r
1148   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1149   wp.rcNormalPosition.left = wpMain.x;\r
1150   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1151   wp.rcNormalPosition.top = wpMain.y;\r
1152   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1153   SetWindowPlacement(hwndMain, &wp);\r
1154 \r
1155   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1156 \r
1157   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1158                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1159 \r
1160   if (hwndConsole) {\r
1161 #if AOT_CONSOLE\r
1162     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1163                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1164 #endif\r
1165     ShowWindow(hwndConsole, nCmdShow);\r
1166     SetActiveWindow(hwndConsole);\r
1167   }\r
1168   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1169   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1170 \r
1171   return TRUE;\r
1172 \r
1173 }\r
1174 \r
1175 VOID\r
1176 InitMenuChecks()\r
1177 {\r
1178   HMENU hmenu = GetMenu(hwndMain);\r
1179 \r
1180   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1181                         MF_BYCOMMAND|((appData.icsActive &&\r
1182                                        *appData.icsCommPort != NULLCHAR) ?\r
1183                                       MF_ENABLED : MF_GRAYED));\r
1184   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1185                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1186                                      MF_CHECKED : MF_UNCHECKED));\r
1187 }\r
1188 \r
1189 //---------------------------------------------------------------------------------------------------------\r
1190 \r
1191 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1192 #define XBOARD FALSE\r
1193 \r
1194 #define OPTCHAR "/"\r
1195 #define SEPCHAR "="\r
1196 \r
1197 #include "args.h"\r
1198 \r
1199 // front-end part of option handling\r
1200 \r
1201 VOID\r
1202 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1203 {\r
1204   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1205   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1206   DeleteDC(hdc);\r
1207   lf->lfWidth = 0;\r
1208   lf->lfEscapement = 0;\r
1209   lf->lfOrientation = 0;\r
1210   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1211   lf->lfItalic = mfp->italic;\r
1212   lf->lfUnderline = mfp->underline;\r
1213   lf->lfStrikeOut = mfp->strikeout;\r
1214   lf->lfCharSet = mfp->charset;\r
1215   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1216   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1217   lf->lfQuality = DEFAULT_QUALITY;\r
1218   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1219     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1220 }\r
1221 \r
1222 void\r
1223 CreateFontInMF(MyFont *mf)\r
1224\r
1225   LFfromMFP(&mf->lf, &mf->mfp);\r
1226   if (mf->hf) DeleteObject(mf->hf);\r
1227   mf->hf = CreateFontIndirect(&mf->lf);\r
1228 }\r
1229 \r
1230 // [HGM] This platform-dependent table provides the location for storing the color info\r
1231 void *\r
1232 colorVariable[] = {\r
1233   &whitePieceColor, \r
1234   &blackPieceColor, \r
1235   &lightSquareColor,\r
1236   &darkSquareColor, \r
1237   &highlightSquareColor,\r
1238   &premoveHighlightColor,\r
1239   NULL,\r
1240   &consoleBackgroundColor,\r
1241   &appData.fontForeColorWhite,\r
1242   &appData.fontBackColorWhite,\r
1243   &appData.fontForeColorBlack,\r
1244   &appData.fontBackColorBlack,\r
1245   &appData.evalHistColorWhite,\r
1246   &appData.evalHistColorBlack,\r
1247   &appData.highlightArrowColor,\r
1248 };\r
1249 \r
1250 /* Command line font name parser.  NULL name means do nothing.\r
1251    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1252    For backward compatibility, syntax without the colon is also\r
1253    accepted, but font names with digits in them won't work in that case.\r
1254 */\r
1255 VOID\r
1256 ParseFontName(char *name, MyFontParams *mfp)\r
1257 {\r
1258   char *p, *q;\r
1259   if (name == NULL) return;\r
1260   p = name;\r
1261   q = strchr(p, ':');\r
1262   if (q) {\r
1263     if (q - p >= sizeof(mfp->faceName))\r
1264       ExitArgError(_("Font name too long:"), name, TRUE);\r
1265     memcpy(mfp->faceName, p, q - p);\r
1266     mfp->faceName[q - p] = NULLCHAR;\r
1267     p = q + 1;\r
1268   } else {\r
1269     q = mfp->faceName;\r
1270     while (*p && !isdigit(*p)) {\r
1271       *q++ = *p++;\r
1272       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1273         ExitArgError(_("Font name too long:"), name, TRUE);\r
1274     }\r
1275     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1276     *q = NULLCHAR;\r
1277   }\r
1278   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1279   mfp->pointSize = (float) atof(p);\r
1280   mfp->bold = (strchr(p, 'b') != NULL);\r
1281   mfp->italic = (strchr(p, 'i') != NULL);\r
1282   mfp->underline = (strchr(p, 'u') != NULL);\r
1283   mfp->strikeout = (strchr(p, 's') != NULL);\r
1284   mfp->charset = DEFAULT_CHARSET;\r
1285   q = strchr(p, 'c');\r
1286   if (q)\r
1287     mfp->charset = (BYTE) atoi(q+1);\r
1288 }\r
1289 \r
1290 void\r
1291 ParseFont(char *name, int number)\r
1292 { // wrapper to shield back-end from 'font'\r
1293   ParseFontName(name, &font[boardSize][number]->mfp);\r
1294 }\r
1295 \r
1296 void\r
1297 SetFontDefaults()\r
1298 { // in WB  we have a 2D array of fonts; this initializes their description\r
1299   int i, j;\r
1300   /* Point font array elements to structures and\r
1301      parse default font names */\r
1302   for (i=0; i<NUM_FONTS; i++) {\r
1303     for (j=0; j<NUM_SIZES; j++) {\r
1304       font[j][i] = &fontRec[j][i];\r
1305       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1306     }\r
1307   }\r
1308 }\r
1309 \r
1310 void\r
1311 CreateFonts()\r
1312 { // here we create the actual fonts from the selected descriptions\r
1313   int i, j;\r
1314   for (i=0; i<NUM_FONTS; i++) {\r
1315     for (j=0; j<NUM_SIZES; j++) {\r
1316       CreateFontInMF(font[j][i]);\r
1317     }\r
1318   }\r
1319 }\r
1320 /* Color name parser.\r
1321    X version accepts X color names, but this one\r
1322    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1323 COLORREF\r
1324 ParseColorName(char *name)\r
1325 {\r
1326   int red, green, blue, count;\r
1327   char buf[MSG_SIZ];\r
1328 \r
1329   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1330   if (count != 3) {\r
1331     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1332       &red, &green, &blue);\r
1333   }\r
1334   if (count != 3) {\r
1335     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1336     DisplayError(buf, 0);\r
1337     return RGB(0, 0, 0);\r
1338   }\r
1339   return PALETTERGB(red, green, blue);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseColor(int n, char *name)\r
1344 { // for WinBoard the color is an int, which needs to be derived from the string\r
1345   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1346 }\r
1347 \r
1348 void\r
1349 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1350 {\r
1351   char *e = argValue;\r
1352   int eff = 0;\r
1353 \r
1354   while (*e) {\r
1355     if (*e == 'b')      eff |= CFE_BOLD;\r
1356     else if (*e == 'i') eff |= CFE_ITALIC;\r
1357     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1358     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1359     else if (*e == '#' || isdigit(*e)) break;\r
1360     e++;\r
1361   }\r
1362   *effects = eff;\r
1363   *color   = ParseColorName(e);\r
1364 }\r
1365 \r
1366 void\r
1367 ParseTextAttribs(ColorClass cc, char *s)\r
1368 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1369     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1370     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1371 }\r
1372 \r
1373 void\r
1374 ParseBoardSize(void *addr, char *name)\r
1375 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1376   BoardSize bs = SizeTiny;\r
1377   while (sizeInfo[bs].name != NULL) {\r
1378     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1379         *(BoardSize *)addr = bs;\r
1380         return;\r
1381     }\r
1382     bs++;\r
1383   }\r
1384   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1385 }\r
1386 \r
1387 void\r
1388 LoadAllSounds()\r
1389 { // [HGM] import name from appData first\r
1390   ColorClass cc;\r
1391   SoundClass sc;\r
1392   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1393     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1394     textAttribs[cc].sound.data = NULL;\r
1395     MyLoadSound(&textAttribs[cc].sound);\r
1396   }\r
1397   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1398     textAttribs[cc].sound.name = strdup("");\r
1399     textAttribs[cc].sound.data = NULL;\r
1400   }\r
1401   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1402     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1403     sounds[sc].data = NULL;\r
1404     MyLoadSound(&sounds[sc]);\r
1405   }\r
1406 }\r
1407 \r
1408 void\r
1409 SetCommPortDefaults()\r
1410 {\r
1411    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1412   dcb.DCBlength = sizeof(DCB);\r
1413   dcb.BaudRate = 9600;\r
1414   dcb.fBinary = TRUE;\r
1415   dcb.fParity = FALSE;\r
1416   dcb.fOutxCtsFlow = FALSE;\r
1417   dcb.fOutxDsrFlow = FALSE;\r
1418   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1419   dcb.fDsrSensitivity = FALSE;\r
1420   dcb.fTXContinueOnXoff = TRUE;\r
1421   dcb.fOutX = FALSE;\r
1422   dcb.fInX = FALSE;\r
1423   dcb.fNull = FALSE;\r
1424   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1425   dcb.fAbortOnError = FALSE;\r
1426   dcb.ByteSize = 7;\r
1427   dcb.Parity = SPACEPARITY;\r
1428   dcb.StopBits = ONESTOPBIT;\r
1429 }\r
1430 \r
1431 // [HGM] args: these three cases taken out to stay in front-end\r
1432 void\r
1433 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1434 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1435         // while the curent board size determines the element. This system should be ported to XBoard.\r
1436         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1437         int bs;\r
1438         for (bs=0; bs<NUM_SIZES; bs++) {\r
1439           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1440           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1441           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1442             ad->argName, mfp->faceName, mfp->pointSize,\r
1443             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1444             mfp->bold ? "b" : "",\r
1445             mfp->italic ? "i" : "",\r
1446             mfp->underline ? "u" : "",\r
1447             mfp->strikeout ? "s" : "",\r
1448             (int)mfp->charset);\r
1449         }\r
1450       }\r
1451 \r
1452 void\r
1453 ExportSounds()\r
1454 { // [HGM] copy the names from the internal WB variables to appData\r
1455   ColorClass cc;\r
1456   SoundClass sc;\r
1457   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1458     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1459   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1460     (&appData.soundMove)[sc] = sounds[sc].name;\r
1461 }\r
1462 \r
1463 void\r
1464 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1465 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1466         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1467         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1468           (ta->effects & CFE_BOLD) ? "b" : "",\r
1469           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1470           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1471           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1472           (ta->effects) ? " " : "",\r
1473           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1474       }\r
1475 \r
1476 void\r
1477 SaveColor(FILE *f, ArgDescriptor *ad)\r
1478 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1479         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1480         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1481           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1482 }\r
1483 \r
1484 void\r
1485 SaveBoardSize(FILE *f, char *name, void *addr)\r
1486 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1487   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1488 }\r
1489 \r
1490 void\r
1491 ParseCommPortSettings(char *s)\r
1492 { // wrapper to keep dcb from back-end\r
1493   ParseCommSettings(s, &dcb);\r
1494 }\r
1495 \r
1496 void\r
1497 GetWindowCoords()\r
1498 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1499   GetActualPlacement(hwndMain, &wpMain);\r
1500   GetActualPlacement(hwndConsole, &wpConsole);\r
1501   GetActualPlacement(commentDialog, &wpComment);\r
1502   GetActualPlacement(editTagsDialog, &wpTags);\r
1503   GetActualPlacement(gameListDialog, &wpGameList);\r
1504   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1505   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1506   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1507 }\r
1508 \r
1509 void\r
1510 PrintCommPortSettings(FILE *f, char *name)\r
1511 { // wrapper to shield back-end from DCB\r
1512       PrintCommSettings(f, name, &dcb);\r
1513 }\r
1514 \r
1515 int\r
1516 MySearchPath(char *installDir, char *name, char *fullname)\r
1517 {\r
1518   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1519   if(name[0]== '%') {\r
1520     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1521     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1522       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1523       *strchr(buf, '%') = 0;\r
1524       strcat(fullname, getenv(buf));\r
1525       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1526     }\r
1527     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1528     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1529     return (int) strlen(fullname);\r
1530   }\r
1531   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1532 }\r
1533 \r
1534 int\r
1535 MyGetFullPathName(char *name, char *fullname)\r
1536 {\r
1537   char *dummy;\r
1538   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1539 }\r
1540 \r
1541 int\r
1542 MainWindowUp()\r
1543 { // [HGM] args: allows testing if main window is realized from back-end\r
1544   return hwndMain != NULL;\r
1545 }\r
1546 \r
1547 void\r
1548 PopUpStartupDialog()\r
1549 {\r
1550     FARPROC lpProc;\r
1551     \r
1552     LoadLanguageFile(appData.language);\r
1553     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1554     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1555     FreeProcInstance(lpProc);\r
1556 }\r
1557 \r
1558 /*---------------------------------------------------------------------------*\\r
1559  *\r
1560  * GDI board drawing routines\r
1561  *\r
1562 \*---------------------------------------------------------------------------*/\r
1563 \r
1564 /* [AS] Draw square using background texture */\r
1565 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1566 {\r
1567     XFORM   x;\r
1568 \r
1569     if( mode == 0 ) {\r
1570         return; /* Should never happen! */\r
1571     }\r
1572 \r
1573     SetGraphicsMode( dst, GM_ADVANCED );\r
1574 \r
1575     switch( mode ) {\r
1576     case 1:\r
1577         /* Identity */\r
1578         break;\r
1579     case 2:\r
1580         /* X reflection */\r
1581         x.eM11 = -1.0;\r
1582         x.eM12 = 0;\r
1583         x.eM21 = 0;\r
1584         x.eM22 = 1.0;\r
1585         x.eDx = (FLOAT) dw + dx - 1;\r
1586         x.eDy = 0;\r
1587         dx = 0;\r
1588         SetWorldTransform( dst, &x );\r
1589         break;\r
1590     case 3:\r
1591         /* Y reflection */\r
1592         x.eM11 = 1.0;\r
1593         x.eM12 = 0;\r
1594         x.eM21 = 0;\r
1595         x.eM22 = -1.0;\r
1596         x.eDx = 0;\r
1597         x.eDy = (FLOAT) dh + dy - 1;\r
1598         dy = 0;\r
1599         SetWorldTransform( dst, &x );\r
1600         break;\r
1601     case 4:\r
1602         /* X/Y flip */\r
1603         x.eM11 = 0;\r
1604         x.eM12 = 1.0;\r
1605         x.eM21 = 1.0;\r
1606         x.eM22 = 0;\r
1607         x.eDx = (FLOAT) dx;\r
1608         x.eDy = (FLOAT) dy;\r
1609         dx = 0;\r
1610         dy = 0;\r
1611         SetWorldTransform( dst, &x );\r
1612         break;\r
1613     }\r
1614 \r
1615     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1616 \r
1617     x.eM11 = 1.0;\r
1618     x.eM12 = 0;\r
1619     x.eM21 = 0;\r
1620     x.eM22 = 1.0;\r
1621     x.eDx = 0;\r
1622     x.eDy = 0;\r
1623     SetWorldTransform( dst, &x );\r
1624 \r
1625     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1626 }\r
1627 \r
1628 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1629 enum {\r
1630     PM_WP = (int) WhitePawn, \r
1631     PM_WN = (int) WhiteKnight, \r
1632     PM_WB = (int) WhiteBishop, \r
1633     PM_WR = (int) WhiteRook, \r
1634     PM_WQ = (int) WhiteQueen, \r
1635     PM_WF = (int) WhiteFerz, \r
1636     PM_WW = (int) WhiteWazir, \r
1637     PM_WE = (int) WhiteAlfil, \r
1638     PM_WM = (int) WhiteMan, \r
1639     PM_WO = (int) WhiteCannon, \r
1640     PM_WU = (int) WhiteUnicorn, \r
1641     PM_WH = (int) WhiteNightrider, \r
1642     PM_WA = (int) WhiteAngel, \r
1643     PM_WC = (int) WhiteMarshall, \r
1644     PM_WAB = (int) WhiteCardinal, \r
1645     PM_WD = (int) WhiteDragon, \r
1646     PM_WL = (int) WhiteLance, \r
1647     PM_WS = (int) WhiteCobra, \r
1648     PM_WV = (int) WhiteFalcon, \r
1649     PM_WSG = (int) WhiteSilver, \r
1650     PM_WG = (int) WhiteGrasshopper, \r
1651     PM_WK = (int) WhiteKing,\r
1652     PM_BP = (int) BlackPawn, \r
1653     PM_BN = (int) BlackKnight, \r
1654     PM_BB = (int) BlackBishop, \r
1655     PM_BR = (int) BlackRook, \r
1656     PM_BQ = (int) BlackQueen, \r
1657     PM_BF = (int) BlackFerz, \r
1658     PM_BW = (int) BlackWazir, \r
1659     PM_BE = (int) BlackAlfil, \r
1660     PM_BM = (int) BlackMan,\r
1661     PM_BO = (int) BlackCannon, \r
1662     PM_BU = (int) BlackUnicorn, \r
1663     PM_BH = (int) BlackNightrider, \r
1664     PM_BA = (int) BlackAngel, \r
1665     PM_BC = (int) BlackMarshall, \r
1666     PM_BG = (int) BlackGrasshopper, \r
1667     PM_BAB = (int) BlackCardinal,\r
1668     PM_BD = (int) BlackDragon,\r
1669     PM_BL = (int) BlackLance,\r
1670     PM_BS = (int) BlackCobra,\r
1671     PM_BV = (int) BlackFalcon,\r
1672     PM_BSG = (int) BlackSilver,\r
1673     PM_BK = (int) BlackKing\r
1674 };\r
1675 \r
1676 static HFONT hPieceFont = NULL;\r
1677 static HBITMAP hPieceMask[(int) EmptySquare];\r
1678 static HBITMAP hPieceFace[(int) EmptySquare];\r
1679 static int fontBitmapSquareSize = 0;\r
1680 static char pieceToFontChar[(int) EmptySquare] =\r
1681                               { 'p', 'n', 'b', 'r', 'q', \r
1682                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1683                       'k', 'o', 'm', 'v', 't', 'w', \r
1684                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1685                                                               'l' };\r
1686 \r
1687 extern BOOL SetCharTable( char *table, const char * map );\r
1688 /* [HGM] moved to backend.c */\r
1689 \r
1690 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1691 {\r
1692     HBRUSH hbrush;\r
1693     BYTE r1 = GetRValue( color );\r
1694     BYTE g1 = GetGValue( color );\r
1695     BYTE b1 = GetBValue( color );\r
1696     BYTE r2 = r1 / 2;\r
1697     BYTE g2 = g1 / 2;\r
1698     BYTE b2 = b1 / 2;\r
1699     RECT rc;\r
1700 \r
1701     /* Create a uniform background first */\r
1702     hbrush = CreateSolidBrush( color );\r
1703     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1704     FillRect( hdc, &rc, hbrush );\r
1705     DeleteObject( hbrush );\r
1706     \r
1707     if( mode == 1 ) {\r
1708         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1709         int steps = squareSize / 2;\r
1710         int i;\r
1711 \r
1712         for( i=0; i<steps; i++ ) {\r
1713             BYTE r = r1 - (r1-r2) * i / steps;\r
1714             BYTE g = g1 - (g1-g2) * i / steps;\r
1715             BYTE b = b1 - (b1-b2) * i / steps;\r
1716 \r
1717             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1718             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1719             FillRect( hdc, &rc, hbrush );\r
1720             DeleteObject(hbrush);\r
1721         }\r
1722     }\r
1723     else if( mode == 2 ) {\r
1724         /* Diagonal gradient, good more or less for every piece */\r
1725         POINT triangle[3];\r
1726         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1727         HBRUSH hbrush_old;\r
1728         int steps = squareSize;\r
1729         int i;\r
1730 \r
1731         triangle[0].x = squareSize - steps;\r
1732         triangle[0].y = squareSize;\r
1733         triangle[1].x = squareSize;\r
1734         triangle[1].y = squareSize;\r
1735         triangle[2].x = squareSize;\r
1736         triangle[2].y = squareSize - steps;\r
1737 \r
1738         for( i=0; i<steps; i++ ) {\r
1739             BYTE r = r1 - (r1-r2) * i / steps;\r
1740             BYTE g = g1 - (g1-g2) * i / steps;\r
1741             BYTE b = b1 - (b1-b2) * i / steps;\r
1742 \r
1743             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1744             hbrush_old = SelectObject( hdc, hbrush );\r
1745             Polygon( hdc, triangle, 3 );\r
1746             SelectObject( hdc, hbrush_old );\r
1747             DeleteObject(hbrush);\r
1748             triangle[0].x++;\r
1749             triangle[2].y++;\r
1750         }\r
1751 \r
1752         SelectObject( hdc, hpen );\r
1753     }\r
1754 }\r
1755 \r
1756 /*\r
1757     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1758     seems to work ok. The main problem here is to find the "inside" of a chess\r
1759     piece: follow the steps as explained below.\r
1760 */\r
1761 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1762 {\r
1763     HBITMAP hbm;\r
1764     HBITMAP hbm_old;\r
1765     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1766     RECT rc;\r
1767     SIZE sz;\r
1768     POINT pt;\r
1769     int backColor = whitePieceColor; \r
1770     int foreColor = blackPieceColor;\r
1771     \r
1772     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1773         backColor = appData.fontBackColorWhite;\r
1774         foreColor = appData.fontForeColorWhite;\r
1775     }\r
1776     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1777         backColor = appData.fontBackColorBlack;\r
1778         foreColor = appData.fontForeColorBlack;\r
1779     }\r
1780 \r
1781     /* Mask */\r
1782     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1783 \r
1784     hbm_old = SelectObject( hdc, hbm );\r
1785 \r
1786     rc.left = 0;\r
1787     rc.top = 0;\r
1788     rc.right = squareSize;\r
1789     rc.bottom = squareSize;\r
1790 \r
1791     /* Step 1: background is now black */\r
1792     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1793 \r
1794     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1795 \r
1796     pt.x = (squareSize - sz.cx) / 2;\r
1797     pt.y = (squareSize - sz.cy) / 2;\r
1798 \r
1799     SetBkMode( hdc, TRANSPARENT );\r
1800     SetTextColor( hdc, chroma );\r
1801     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1802     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1803 \r
1804     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1805     /* Step 3: the area outside the piece is filled with white */\r
1806 //    FloodFill( hdc, 0, 0, chroma );\r
1807     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1808     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1809     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1810     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1811     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1812     /* \r
1813         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1814         but if the start point is not inside the piece we're lost!\r
1815         There should be a better way to do this... if we could create a region or path\r
1816         from the fill operation we would be fine for example.\r
1817     */\r
1818 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1819     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1820 \r
1821     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1822         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1823         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1824 \r
1825         SelectObject( dc2, bm2 );\r
1826         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1827         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1828         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1829         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1830         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1831 \r
1832         DeleteDC( dc2 );\r
1833         DeleteObject( bm2 );\r
1834     }\r
1835 \r
1836     SetTextColor( hdc, 0 );\r
1837     /* \r
1838         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1839         draw the piece again in black for safety.\r
1840     */\r
1841     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1842 \r
1843     SelectObject( hdc, hbm_old );\r
1844 \r
1845     if( hPieceMask[index] != NULL ) {\r
1846         DeleteObject( hPieceMask[index] );\r
1847     }\r
1848 \r
1849     hPieceMask[index] = hbm;\r
1850 \r
1851     /* Face */\r
1852     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1853 \r
1854     SelectObject( hdc, hbm );\r
1855 \r
1856     {\r
1857         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1858         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1859         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1860 \r
1861         SelectObject( dc1, hPieceMask[index] );\r
1862         SelectObject( dc2, bm2 );\r
1863         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1864         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1865         \r
1866         /* \r
1867             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1868             the piece background and deletes (makes transparent) the rest.\r
1869             Thanks to that mask, we are free to paint the background with the greates\r
1870             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1871             We use this, to make gradients and give the pieces a "roundish" look.\r
1872         */\r
1873         SetPieceBackground( hdc, backColor, 2 );\r
1874         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1875 \r
1876         DeleteDC( dc2 );\r
1877         DeleteDC( dc1 );\r
1878         DeleteObject( bm2 );\r
1879     }\r
1880 \r
1881     SetTextColor( hdc, foreColor );\r
1882     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1883 \r
1884     SelectObject( hdc, hbm_old );\r
1885 \r
1886     if( hPieceFace[index] != NULL ) {\r
1887         DeleteObject( hPieceFace[index] );\r
1888     }\r
1889 \r
1890     hPieceFace[index] = hbm;\r
1891 }\r
1892 \r
1893 static int TranslatePieceToFontPiece( int piece )\r
1894 {\r
1895     switch( piece ) {\r
1896     case BlackPawn:\r
1897         return PM_BP;\r
1898     case BlackKnight:\r
1899         return PM_BN;\r
1900     case BlackBishop:\r
1901         return PM_BB;\r
1902     case BlackRook:\r
1903         return PM_BR;\r
1904     case BlackQueen:\r
1905         return PM_BQ;\r
1906     case BlackKing:\r
1907         return PM_BK;\r
1908     case WhitePawn:\r
1909         return PM_WP;\r
1910     case WhiteKnight:\r
1911         return PM_WN;\r
1912     case WhiteBishop:\r
1913         return PM_WB;\r
1914     case WhiteRook:\r
1915         return PM_WR;\r
1916     case WhiteQueen:\r
1917         return PM_WQ;\r
1918     case WhiteKing:\r
1919         return PM_WK;\r
1920 \r
1921     case BlackAngel:\r
1922         return PM_BA;\r
1923     case BlackMarshall:\r
1924         return PM_BC;\r
1925     case BlackFerz:\r
1926         return PM_BF;\r
1927     case BlackNightrider:\r
1928         return PM_BH;\r
1929     case BlackAlfil:\r
1930         return PM_BE;\r
1931     case BlackWazir:\r
1932         return PM_BW;\r
1933     case BlackUnicorn:\r
1934         return PM_BU;\r
1935     case BlackCannon:\r
1936         return PM_BO;\r
1937     case BlackGrasshopper:\r
1938         return PM_BG;\r
1939     case BlackMan:\r
1940         return PM_BM;\r
1941     case BlackSilver:\r
1942         return PM_BSG;\r
1943     case BlackLance:\r
1944         return PM_BL;\r
1945     case BlackFalcon:\r
1946         return PM_BV;\r
1947     case BlackCobra:\r
1948         return PM_BS;\r
1949     case BlackCardinal:\r
1950         return PM_BAB;\r
1951     case BlackDragon:\r
1952         return PM_BD;\r
1953 \r
1954     case WhiteAngel:\r
1955         return PM_WA;\r
1956     case WhiteMarshall:\r
1957         return PM_WC;\r
1958     case WhiteFerz:\r
1959         return PM_WF;\r
1960     case WhiteNightrider:\r
1961         return PM_WH;\r
1962     case WhiteAlfil:\r
1963         return PM_WE;\r
1964     case WhiteWazir:\r
1965         return PM_WW;\r
1966     case WhiteUnicorn:\r
1967         return PM_WU;\r
1968     case WhiteCannon:\r
1969         return PM_WO;\r
1970     case WhiteGrasshopper:\r
1971         return PM_WG;\r
1972     case WhiteMan:\r
1973         return PM_WM;\r
1974     case WhiteSilver:\r
1975         return PM_WSG;\r
1976     case WhiteLance:\r
1977         return PM_WL;\r
1978     case WhiteFalcon:\r
1979         return PM_WV;\r
1980     case WhiteCobra:\r
1981         return PM_WS;\r
1982     case WhiteCardinal:\r
1983         return PM_WAB;\r
1984     case WhiteDragon:\r
1985         return PM_WD;\r
1986     }\r
1987 \r
1988     return 0;\r
1989 }\r
1990 \r
1991 void CreatePiecesFromFont()\r
1992 {\r
1993     LOGFONT lf;\r
1994     HDC hdc_window = NULL;\r
1995     HDC hdc = NULL;\r
1996     HFONT hfont_old;\r
1997     int fontHeight;\r
1998     int i;\r
1999 \r
2000     if( fontBitmapSquareSize < 0 ) {\r
2001         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2002         return;\r
2003     }\r
2004 \r
2005     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2006             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2007         fontBitmapSquareSize = -1;\r
2008         return;\r
2009     }\r
2010 \r
2011     if( fontBitmapSquareSize != squareSize ) {\r
2012         hdc_window = GetDC( hwndMain );\r
2013         hdc = CreateCompatibleDC( hdc_window );\r
2014 \r
2015         if( hPieceFont != NULL ) {\r
2016             DeleteObject( hPieceFont );\r
2017         }\r
2018         else {\r
2019             for( i=0; i<=(int)BlackKing; i++ ) {\r
2020                 hPieceMask[i] = NULL;\r
2021                 hPieceFace[i] = NULL;\r
2022             }\r
2023         }\r
2024 \r
2025         fontHeight = 75;\r
2026 \r
2027         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2028             fontHeight = appData.fontPieceSize;\r
2029         }\r
2030 \r
2031         fontHeight = (fontHeight * squareSize) / 100;\r
2032 \r
2033         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2034         lf.lfWidth = 0;\r
2035         lf.lfEscapement = 0;\r
2036         lf.lfOrientation = 0;\r
2037         lf.lfWeight = FW_NORMAL;\r
2038         lf.lfItalic = 0;\r
2039         lf.lfUnderline = 0;\r
2040         lf.lfStrikeOut = 0;\r
2041         lf.lfCharSet = DEFAULT_CHARSET;\r
2042         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2043         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2044         lf.lfQuality = PROOF_QUALITY;\r
2045         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2046         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2047         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2048 \r
2049         hPieceFont = CreateFontIndirect( &lf );\r
2050 \r
2051         if( hPieceFont == NULL ) {\r
2052             fontBitmapSquareSize = -2;\r
2053         }\r
2054         else {\r
2055             /* Setup font-to-piece character table */\r
2056             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2057                 /* No (or wrong) global settings, try to detect the font */\r
2058                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2059                     /* Alpha */\r
2060                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2061                 }\r
2062                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2063                     /* DiagramTT* family */\r
2064                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2065                 }\r
2066                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2067                     /* Fairy symbols */\r
2068                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2069                 }\r
2070                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2071                     /* Good Companion (Some characters get warped as literal :-( */\r
2072                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2073                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2074                     SetCharTable(pieceToFontChar, s);\r
2075                 }\r
2076                 else {\r
2077                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2078                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2079                 }\r
2080             }\r
2081 \r
2082             /* Create bitmaps */\r
2083             hfont_old = SelectObject( hdc, hPieceFont );\r
2084             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2085                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2086                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2087 \r
2088             SelectObject( hdc, hfont_old );\r
2089 \r
2090             fontBitmapSquareSize = squareSize;\r
2091         }\r
2092     }\r
2093 \r
2094     if( hdc != NULL ) {\r
2095         DeleteDC( hdc );\r
2096     }\r
2097 \r
2098     if( hdc_window != NULL ) {\r
2099         ReleaseDC( hwndMain, hdc_window );\r
2100     }\r
2101 }\r
2102 \r
2103 HBITMAP\r
2104 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2105 {\r
2106   char name[128];\r
2107 \r
2108     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2109   if (gameInfo.event &&\r
2110       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2111       strcmp(name, "k80s") == 0) {\r
2112     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2113   }\r
2114   return LoadBitmap(hinst, name);\r
2115 }\r
2116 \r
2117 \r
2118 /* Insert a color into the program's logical palette\r
2119    structure.  This code assumes the given color is\r
2120    the result of the RGB or PALETTERGB macro, and it\r
2121    knows how those macros work (which is documented).\r
2122 */\r
2123 VOID\r
2124 InsertInPalette(COLORREF color)\r
2125 {\r
2126   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2127 \r
2128   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2129     DisplayFatalError(_("Too many colors"), 0, 1);\r
2130     pLogPal->palNumEntries--;\r
2131     return;\r
2132   }\r
2133 \r
2134   pe->peFlags = (char) 0;\r
2135   pe->peRed = (char) (0xFF & color);\r
2136   pe->peGreen = (char) (0xFF & (color >> 8));\r
2137   pe->peBlue = (char) (0xFF & (color >> 16));\r
2138   return;\r
2139 }\r
2140 \r
2141 \r
2142 VOID\r
2143 InitDrawingColors()\r
2144 {\r
2145   if (pLogPal == NULL) {\r
2146     /* Allocate enough memory for a logical palette with\r
2147      * PALETTESIZE entries and set the size and version fields\r
2148      * of the logical palette structure.\r
2149      */\r
2150     pLogPal = (NPLOGPALETTE)\r
2151       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2152                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2153     pLogPal->palVersion    = 0x300;\r
2154   }\r
2155   pLogPal->palNumEntries = 0;\r
2156 \r
2157   InsertInPalette(lightSquareColor);\r
2158   InsertInPalette(darkSquareColor);\r
2159   InsertInPalette(whitePieceColor);\r
2160   InsertInPalette(blackPieceColor);\r
2161   InsertInPalette(highlightSquareColor);\r
2162   InsertInPalette(premoveHighlightColor);\r
2163 \r
2164   /*  create a logical color palette according the information\r
2165    *  in the LOGPALETTE structure.\r
2166    */\r
2167   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2168 \r
2169   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2170   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2171   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2172   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2173   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2174   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2175   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2176   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2177   /* [AS] Force rendering of the font-based pieces */\r
2178   if( fontBitmapSquareSize > 0 ) {\r
2179     fontBitmapSquareSize = 0;\r
2180   }\r
2181 }\r
2182 \r
2183 \r
2184 int\r
2185 BoardWidth(int boardSize, int n)\r
2186 { /* [HGM] argument n added to allow different width and height */\r
2187   int lineGap = sizeInfo[boardSize].lineGap;\r
2188 \r
2189   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2190       lineGap = appData.overrideLineGap;\r
2191   }\r
2192 \r
2193   return (n + 1) * lineGap +\r
2194           n * sizeInfo[boardSize].squareSize;\r
2195 }\r
2196 \r
2197 /* Respond to board resize by dragging edge */\r
2198 VOID\r
2199 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2200 {\r
2201   BoardSize newSize = NUM_SIZES - 1;\r
2202   static int recurse = 0;\r
2203   if (IsIconic(hwndMain)) return;\r
2204   if (recurse > 0) return;\r
2205   recurse++;\r
2206   while (newSize > 0) {\r
2207         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2208         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2209            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2210     newSize--;\r
2211   } \r
2212   boardSize = newSize;\r
2213   InitDrawingSizes(boardSize, flags);\r
2214   recurse--;\r
2215 }\r
2216 \r
2217 \r
2218 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2219 \r
2220 VOID\r
2221 InitDrawingSizes(BoardSize boardSize, int flags)\r
2222 {\r
2223   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2224   ChessSquare piece;\r
2225   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2226   HDC hdc;\r
2227   SIZE clockSize, messageSize;\r
2228   HFONT oldFont;\r
2229   char buf[MSG_SIZ];\r
2230   char *str;\r
2231   HMENU hmenu = GetMenu(hwndMain);\r
2232   RECT crect, wrect, oldRect;\r
2233   int offby;\r
2234   LOGBRUSH logbrush;\r
2235 \r
2236   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2237   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2238 \r
2239   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2240   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2241 \r
2242   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2243   oldRect.top = wpMain.y;\r
2244   oldRect.right = wpMain.x + wpMain.width;\r
2245   oldRect.bottom = wpMain.y + wpMain.height;\r
2246 \r
2247   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2248   smallLayout = sizeInfo[boardSize].smallLayout;\r
2249   squareSize = sizeInfo[boardSize].squareSize;\r
2250   lineGap = sizeInfo[boardSize].lineGap;\r
2251   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2252 \r
2253   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2254       lineGap = appData.overrideLineGap;\r
2255   }\r
2256 \r
2257   if (tinyLayout != oldTinyLayout) {\r
2258     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2259     if (tinyLayout) {\r
2260       style &= ~WS_SYSMENU;\r
2261       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2262                  "&Minimize\tCtrl+F4");\r
2263     } else {\r
2264       style |= WS_SYSMENU;\r
2265       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2266     }\r
2267     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2268 \r
2269     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2270       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2271         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2272     }\r
2273     DrawMenuBar(hwndMain);\r
2274   }\r
2275 \r
2276   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2277   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2278 \r
2279   /* Get text area sizes */\r
2280   hdc = GetDC(hwndMain);\r
2281   if (appData.clockMode) {\r
2282     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2283   } else {\r
2284     snprintf(buf, MSG_SIZ, _("White"));\r
2285   }\r
2286   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2287   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2288   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2289   str = _("We only care about the height here");\r
2290   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2291   SelectObject(hdc, oldFont);\r
2292   ReleaseDC(hwndMain, hdc);\r
2293 \r
2294   /* Compute where everything goes */\r
2295   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2296         /* [HGM] logo: if either logo is on, reserve space for it */\r
2297         logoHeight =  2*clockSize.cy;\r
2298         leftLogoRect.left   = OUTER_MARGIN;\r
2299         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2300         leftLogoRect.top    = OUTER_MARGIN;\r
2301         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2302 \r
2303         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2304         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2305         rightLogoRect.top    = OUTER_MARGIN;\r
2306         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2307 \r
2308 \r
2309     whiteRect.left = leftLogoRect.right;\r
2310     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2311     whiteRect.top = OUTER_MARGIN;\r
2312     whiteRect.bottom = whiteRect.top + logoHeight;\r
2313 \r
2314     blackRect.right = rightLogoRect.left;\r
2315     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2316     blackRect.top = whiteRect.top;\r
2317     blackRect.bottom = whiteRect.bottom;\r
2318   } else {\r
2319     whiteRect.left = OUTER_MARGIN;\r
2320     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2321     whiteRect.top = OUTER_MARGIN;\r
2322     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2323 \r
2324     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2325     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2326     blackRect.top = whiteRect.top;\r
2327     blackRect.bottom = whiteRect.bottom;\r
2328 \r
2329     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2330   }\r
2331 \r
2332   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2333   if (appData.showButtonBar) {\r
2334     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2335       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2336   } else {\r
2337     messageRect.right = OUTER_MARGIN + boardWidth;\r
2338   }\r
2339   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2340   messageRect.bottom = messageRect.top + messageSize.cy;\r
2341 \r
2342   boardRect.left = OUTER_MARGIN;\r
2343   boardRect.right = boardRect.left + boardWidth;\r
2344   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2345   boardRect.bottom = boardRect.top + boardHeight;\r
2346 \r
2347   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2348   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2349   oldBoardSize = boardSize;\r
2350   oldTinyLayout = tinyLayout;\r
2351   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2352   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2353     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2354   winW *= 1 + twoBoards;\r
2355   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2356   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2357   wpMain.height = winH; //       without disturbing window attachments\r
2358   GetWindowRect(hwndMain, &wrect);\r
2359   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2360                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2361 \r
2362   // [HGM] placement: let attached windows follow size change.\r
2363   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2364   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2365   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2366   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2367   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2368 \r
2369   /* compensate if menu bar wrapped */\r
2370   GetClientRect(hwndMain, &crect);\r
2371   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2372   wpMain.height += offby;\r
2373   switch (flags) {\r
2374   case WMSZ_TOPLEFT:\r
2375     SetWindowPos(hwndMain, NULL, \r
2376                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2377                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2378     break;\r
2379 \r
2380   case WMSZ_TOPRIGHT:\r
2381   case WMSZ_TOP:\r
2382     SetWindowPos(hwndMain, NULL, \r
2383                  wrect.left, wrect.bottom - wpMain.height, \r
2384                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2385     break;\r
2386 \r
2387   case WMSZ_BOTTOMLEFT:\r
2388   case WMSZ_LEFT:\r
2389     SetWindowPos(hwndMain, NULL, \r
2390                  wrect.right - wpMain.width, wrect.top, \r
2391                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2392     break;\r
2393 \r
2394   case WMSZ_BOTTOMRIGHT:\r
2395   case WMSZ_BOTTOM:\r
2396   case WMSZ_RIGHT:\r
2397   default:\r
2398     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2399                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2400     break;\r
2401   }\r
2402 \r
2403   hwndPause = NULL;\r
2404   for (i = 0; i < N_BUTTONS; i++) {\r
2405     if (buttonDesc[i].hwnd != NULL) {\r
2406       DestroyWindow(buttonDesc[i].hwnd);\r
2407       buttonDesc[i].hwnd = NULL;\r
2408     }\r
2409     if (appData.showButtonBar) {\r
2410       buttonDesc[i].hwnd =\r
2411         CreateWindow("BUTTON", buttonDesc[i].label,\r
2412                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2413                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2414                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2415                      (HMENU) buttonDesc[i].id,\r
2416                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2417       if (tinyLayout) {\r
2418         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2419                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2420                     MAKELPARAM(FALSE, 0));\r
2421       }\r
2422       if (buttonDesc[i].id == IDM_Pause)\r
2423         hwndPause = buttonDesc[i].hwnd;\r
2424       buttonDesc[i].wndproc = (WNDPROC)\r
2425         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2426     }\r
2427   }\r
2428   if (gridPen != NULL) DeleteObject(gridPen);\r
2429   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2430   if (premovePen != NULL) DeleteObject(premovePen);\r
2431   if (lineGap != 0) {\r
2432     logbrush.lbStyle = BS_SOLID;\r
2433     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2434     gridPen =\r
2435       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2436                    lineGap, &logbrush, 0, NULL);\r
2437     logbrush.lbColor = highlightSquareColor;\r
2438     highlightPen =\r
2439       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2440                    lineGap, &logbrush, 0, NULL);\r
2441 \r
2442     logbrush.lbColor = premoveHighlightColor; \r
2443     premovePen =\r
2444       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2445                    lineGap, &logbrush, 0, NULL);\r
2446 \r
2447     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2448     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2449       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2450       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2451         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2452       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2453         BOARD_WIDTH * (squareSize + lineGap);\r
2454       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2455     }\r
2456     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2457       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2458       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2459         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2460         lineGap / 2 + (i * (squareSize + lineGap));\r
2461       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2462         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2463       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2464     }\r
2465   }\r
2466 \r
2467   /* [HGM] Licensing requirement */\r
2468 #ifdef GOTHIC\r
2469   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2470 #endif\r
2471 #ifdef FALCON\r
2472   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2473 #endif\r
2474   GothicPopUp( "", VariantNormal);\r
2475 \r
2476 \r
2477 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2478 \r
2479   /* Load piece bitmaps for this board size */\r
2480   for (i=0; i<=2; i++) {\r
2481     for (piece = WhitePawn;\r
2482          (int) piece < (int) BlackPawn;\r
2483          piece = (ChessSquare) ((int) piece + 1)) {\r
2484       if (pieceBitmap[i][piece] != NULL)\r
2485         DeleteObject(pieceBitmap[i][piece]);\r
2486     }\r
2487   }\r
2488 \r
2489   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2490   // Orthodox Chess pieces\r
2491   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2492   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2493   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2494   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2495   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2496   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2497   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2498   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2499   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2500   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2501   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2502   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2503   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2504   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2505   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2506   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2507     // in Shogi, Hijack the unused Queen for Lance\r
2508     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2509     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2510     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2511   } else {\r
2512     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2513     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2514     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2515   }\r
2516 \r
2517   if(squareSize <= 72 && squareSize >= 33) { \r
2518     /* A & C are available in most sizes now */\r
2519     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2520       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2521       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2522       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2523       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2524       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2525       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2526       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2527       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2528       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2529       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2530       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2531       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2532     } else { // Smirf-like\r
2533       if(gameInfo.variant == VariantSChess) {\r
2534         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2535         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2536         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2537       } else {\r
2538         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2539         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2540         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2541       }\r
2542     }\r
2543     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2544       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2545       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2546       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2547     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2548       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2549       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2550       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2551     } else { // WinBoard standard\r
2552       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2553       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2554       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2555     }\r
2556   }\r
2557 \r
2558 \r
2559   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2560     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2561     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2562     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2563     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2564     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2565     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2566     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2567     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2568     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2569     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2570     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2571     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2572     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2573     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2574     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2575     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2576     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2577     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2578     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2579     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2580     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2581     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2582     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2583     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2584     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2585     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2586     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2587     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2588     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2589     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2590 \r
2591     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2592       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2593       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2594       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2595       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2596       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2597       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2598       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2599       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2600       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2601       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2602       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2603       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2604     } else {\r
2605       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2606       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2607       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2608       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2609       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2610       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2611       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2612       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2613       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2614       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2615       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2616       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2617     }\r
2618 \r
2619   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2620     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2621     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2622     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2623     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2624     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2625     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2626     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2627     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2628     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2629     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2630     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2631     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2632     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2633     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2634   }\r
2635 \r
2636 \r
2637   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2638   /* special Shogi support in this size */\r
2639   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2640       for (piece = WhitePawn;\r
2641            (int) piece < (int) BlackPawn;\r
2642            piece = (ChessSquare) ((int) piece + 1)) {\r
2643         if (pieceBitmap[i][piece] != NULL)\r
2644           DeleteObject(pieceBitmap[i][piece]);\r
2645       }\r
2646     }\r
2647   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2648   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2649   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2650   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2651   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2652   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2653   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2654   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2655   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2656   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2657   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2658   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2659   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2660   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2661   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2662   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2663   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2664   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2665   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2666   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2667   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2668   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2669   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2670   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2671   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2672   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2673   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2674   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2675   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2676   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2677   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2678   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2679   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2680   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2681   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2682   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2683   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2684   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2685   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2686   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2687   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2688   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2689   minorSize = 0;\r
2690   }\r
2691 }\r
2692 \r
2693 HBITMAP\r
2694 PieceBitmap(ChessSquare p, int kind)\r
2695 {\r
2696   if ((int) p >= (int) BlackPawn)\r
2697     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2698 \r
2699   return pieceBitmap[kind][(int) p];\r
2700 }\r
2701 \r
2702 /***************************************************************/\r
2703 \r
2704 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2705 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2706 /*\r
2707 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2708 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2709 */\r
2710 \r
2711 VOID\r
2712 SquareToPos(int row, int column, int * x, int * y)\r
2713 {\r
2714   if (flipView) {\r
2715     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2716     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2717   } else {\r
2718     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2719     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2720   }\r
2721 }\r
2722 \r
2723 VOID\r
2724 DrawCoordsOnDC(HDC hdc)\r
2725 {\r
2726   static char files[] = "0123456789012345678901221098765432109876543210";\r
2727   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2728   char str[2] = { NULLCHAR, NULLCHAR };\r
2729   int oldMode, oldAlign, x, y, start, i;\r
2730   HFONT oldFont;\r
2731   HBRUSH oldBrush;\r
2732 \r
2733   if (!appData.showCoords)\r
2734     return;\r
2735 \r
2736   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2737 \r
2738   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2739   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2740   oldAlign = GetTextAlign(hdc);\r
2741   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2742 \r
2743   y = boardRect.top + lineGap;\r
2744   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2745 \r
2746   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2747   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2748     str[0] = files[start + i];\r
2749     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2750     y += squareSize + lineGap;\r
2751   }\r
2752 \r
2753   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2754 \r
2755   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2756   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2757     str[0] = ranks[start + i];\r
2758     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2759     x += squareSize + lineGap;\r
2760   }    \r
2761 \r
2762   SelectObject(hdc, oldBrush);\r
2763   SetBkMode(hdc, oldMode);\r
2764   SetTextAlign(hdc, oldAlign);\r
2765   SelectObject(hdc, oldFont);\r
2766 }\r
2767 \r
2768 VOID\r
2769 DrawGridOnDC(HDC hdc)\r
2770 {\r
2771   HPEN oldPen;\r
2772  \r
2773   if (lineGap != 0) {\r
2774     oldPen = SelectObject(hdc, gridPen);\r
2775     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2776     SelectObject(hdc, oldPen);\r
2777   }\r
2778 }\r
2779 \r
2780 #define HIGHLIGHT_PEN 0\r
2781 #define PREMOVE_PEN   1\r
2782 \r
2783 VOID\r
2784 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2785 {\r
2786   int x1, y1;\r
2787   HPEN oldPen, hPen;\r
2788   if (lineGap == 0) return;\r
2789   if (flipView) {\r
2790     x1 = boardRect.left +\r
2791       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2792     y1 = boardRect.top +\r
2793       lineGap/2 + y * (squareSize + lineGap);\r
2794   } else {\r
2795     x1 = boardRect.left +\r
2796       lineGap/2 + x * (squareSize + lineGap);\r
2797     y1 = boardRect.top +\r
2798       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2799   }\r
2800   hPen = pen ? premovePen : highlightPen;\r
2801   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2802   MoveToEx(hdc, x1, y1, NULL);\r
2803   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2804   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2805   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2806   LineTo(hdc, x1, y1);\r
2807   SelectObject(hdc, oldPen);\r
2808 }\r
2809 \r
2810 VOID\r
2811 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2812 {\r
2813   int i;\r
2814   for (i=0; i<2; i++) {\r
2815     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2816       DrawHighlightOnDC(hdc, TRUE,\r
2817                         h->sq[i].x, h->sq[i].y,\r
2818                         pen);\r
2819   }\r
2820 }\r
2821 \r
2822 /* Note: sqcolor is used only in monoMode */\r
2823 /* Note that this code is largely duplicated in woptions.c,\r
2824    function DrawSampleSquare, so that needs to be updated too */\r
2825 VOID\r
2826 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2827 {\r
2828   HBITMAP oldBitmap;\r
2829   HBRUSH oldBrush;\r
2830   int tmpSize;\r
2831 \r
2832   if (appData.blindfold) return;\r
2833 \r
2834   /* [AS] Use font-based pieces if needed */\r
2835   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2836     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2837     CreatePiecesFromFont();\r
2838 \r
2839     if( fontBitmapSquareSize == squareSize ) {\r
2840         int index = TranslatePieceToFontPiece(piece);\r
2841 \r
2842         SelectObject( tmphdc, hPieceMask[ index ] );\r
2843 \r
2844       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2845         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2846       else\r
2847         BitBlt( hdc,\r
2848             x, y,\r
2849             squareSize, squareSize,\r
2850             tmphdc,\r
2851             0, 0,\r
2852             SRCAND );\r
2853 \r
2854         SelectObject( tmphdc, hPieceFace[ index ] );\r
2855 \r
2856       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2857         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2858       else\r
2859         BitBlt( hdc,\r
2860             x, y,\r
2861             squareSize, squareSize,\r
2862             tmphdc,\r
2863             0, 0,\r
2864             SRCPAINT );\r
2865 \r
2866         return;\r
2867     }\r
2868   }\r
2869 \r
2870   if (appData.monoMode) {\r
2871     SelectObject(tmphdc, PieceBitmap(piece, \r
2872       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2873     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2874            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2875   } else {\r
2876     tmpSize = squareSize;\r
2877     if(minorSize &&\r
2878         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2879          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2880       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2881       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2882       x += (squareSize - minorSize)>>1;\r
2883       y += squareSize - minorSize - 2;\r
2884       tmpSize = minorSize;\r
2885     }\r
2886     if (color || appData.allWhite ) {\r
2887       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2888       if( color )\r
2889               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2890       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2891       if(appData.upsideDown && color==flipView)\r
2892         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2893       else\r
2894         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2895       /* Use black for outline of white pieces */\r
2896       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2897       if(appData.upsideDown && color==flipView)\r
2898         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2899       else\r
2900         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2901     } else {\r
2902       /* Use square color for details of black pieces */\r
2903       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2904       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2905       if(appData.upsideDown && !flipView)\r
2906         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2907       else\r
2908         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2909     }\r
2910     SelectObject(hdc, oldBrush);\r
2911     SelectObject(tmphdc, oldBitmap);\r
2912   }\r
2913 }\r
2914 \r
2915 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2916 int GetBackTextureMode( int algo )\r
2917 {\r
2918     int result = BACK_TEXTURE_MODE_DISABLED;\r
2919 \r
2920     switch( algo ) \r
2921     {\r
2922         case BACK_TEXTURE_MODE_PLAIN:\r
2923             result = 1; /* Always use identity map */\r
2924             break;\r
2925         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2926             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2927             break;\r
2928     }\r
2929 \r
2930     return result;\r
2931 }\r
2932 \r
2933 /* \r
2934     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2935     to handle redraws cleanly (as random numbers would always be different).\r
2936 */\r
2937 VOID RebuildTextureSquareInfo()\r
2938 {\r
2939     BITMAP bi;\r
2940     int lite_w = 0;\r
2941     int lite_h = 0;\r
2942     int dark_w = 0;\r
2943     int dark_h = 0;\r
2944     int row;\r
2945     int col;\r
2946 \r
2947     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2948 \r
2949     if( liteBackTexture != NULL ) {\r
2950         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2951             lite_w = bi.bmWidth;\r
2952             lite_h = bi.bmHeight;\r
2953         }\r
2954     }\r
2955 \r
2956     if( darkBackTexture != NULL ) {\r
2957         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2958             dark_w = bi.bmWidth;\r
2959             dark_h = bi.bmHeight;\r
2960         }\r
2961     }\r
2962 \r
2963     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2964         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2965             if( (col + row) & 1 ) {\r
2966                 /* Lite square */\r
2967                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2968                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2969                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2970                   else\r
2971                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2972                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2973                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2974                   else\r
2975                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2976                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2977                 }\r
2978             }\r
2979             else {\r
2980                 /* Dark square */\r
2981                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2982                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2983                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2984                   else\r
2985                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2986                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2987                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2988                   else\r
2989                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2990                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2991                 }\r
2992             }\r
2993         }\r
2994     }\r
2995 }\r
2996 \r
2997 /* [AS] Arrow highlighting support */\r
2998 \r
2999 static double A_WIDTH = 5; /* Width of arrow body */\r
3000 \r
3001 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3002 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3003 \r
3004 static double Sqr( double x )\r
3005 {\r
3006     return x*x;\r
3007 }\r
3008 \r
3009 static int Round( double x )\r
3010 {\r
3011     return (int) (x + 0.5);\r
3012 }\r
3013 \r
3014 /* Draw an arrow between two points using current settings */\r
3015 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3016 {\r
3017     POINT arrow[7];\r
3018     double dx, dy, j, k, x, y;\r
3019 \r
3020     if( d_x == s_x ) {\r
3021         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3022 \r
3023         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3024         arrow[0].y = s_y;\r
3025 \r
3026         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3027         arrow[1].y = d_y - h;\r
3028 \r
3029         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3030         arrow[2].y = d_y - h;\r
3031 \r
3032         arrow[3].x = d_x;\r
3033         arrow[3].y = d_y;\r
3034 \r
3035         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3036         arrow[5].y = d_y - h;\r
3037 \r
3038         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3039         arrow[4].y = d_y - h;\r
3040 \r
3041         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3042         arrow[6].y = s_y;\r
3043     }\r
3044     else if( d_y == s_y ) {\r
3045         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3046 \r
3047         arrow[0].x = s_x;\r
3048         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3049 \r
3050         arrow[1].x = d_x - w;\r
3051         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3052 \r
3053         arrow[2].x = d_x - w;\r
3054         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3055 \r
3056         arrow[3].x = d_x;\r
3057         arrow[3].y = d_y;\r
3058 \r
3059         arrow[5].x = d_x - w;\r
3060         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3061 \r
3062         arrow[4].x = d_x - w;\r
3063         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3064 \r
3065         arrow[6].x = s_x;\r
3066         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3067     }\r
3068     else {\r
3069         /* [AS] Needed a lot of paper for this! :-) */\r
3070         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3071         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3072   \r
3073         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3074 \r
3075         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3076 \r
3077         x = s_x;\r
3078         y = s_y;\r
3079 \r
3080         arrow[0].x = Round(x - j);\r
3081         arrow[0].y = Round(y + j*dx);\r
3082 \r
3083         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3084         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3085 \r
3086         if( d_x > s_x ) {\r
3087             x = (double) d_x - k;\r
3088             y = (double) d_y - k*dy;\r
3089         }\r
3090         else {\r
3091             x = (double) d_x + k;\r
3092             y = (double) d_y + k*dy;\r
3093         }\r
3094 \r
3095         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3096 \r
3097         arrow[6].x = Round(x - j);\r
3098         arrow[6].y = Round(y + j*dx);\r
3099 \r
3100         arrow[2].x = Round(arrow[6].x + 2*j);\r
3101         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3102 \r
3103         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3104         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3105 \r
3106         arrow[4].x = d_x;\r
3107         arrow[4].y = d_y;\r
3108 \r
3109         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3110         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3111     }\r
3112 \r
3113     Polygon( hdc, arrow, 7 );\r
3114 }\r
3115 \r
3116 /* [AS] Draw an arrow between two squares */\r
3117 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3118 {\r
3119     int s_x, s_y, d_x, d_y;\r
3120     HPEN hpen;\r
3121     HPEN holdpen;\r
3122     HBRUSH hbrush;\r
3123     HBRUSH holdbrush;\r
3124     LOGBRUSH stLB;\r
3125 \r
3126     if( s_col == d_col && s_row == d_row ) {\r
3127         return;\r
3128     }\r
3129 \r
3130     /* Get source and destination points */\r
3131     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3132     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3133 \r
3134     if( d_y > s_y ) {\r
3135         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3136     }\r
3137     else if( d_y < s_y ) {\r
3138         d_y += squareSize / 2 + squareSize / 4;\r
3139     }\r
3140     else {\r
3141         d_y += squareSize / 2;\r
3142     }\r
3143 \r
3144     if( d_x > s_x ) {\r
3145         d_x += squareSize / 2 - squareSize / 4;\r
3146     }\r
3147     else if( d_x < s_x ) {\r
3148         d_x += squareSize / 2 + squareSize / 4;\r
3149     }\r
3150     else {\r
3151         d_x += squareSize / 2;\r
3152     }\r
3153 \r
3154     s_x += squareSize / 2;\r
3155     s_y += squareSize / 2;\r
3156 \r
3157     /* Adjust width */\r
3158     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3159 \r
3160     /* Draw */\r
3161     stLB.lbStyle = BS_SOLID;\r
3162     stLB.lbColor = appData.highlightArrowColor;\r
3163     stLB.lbHatch = 0;\r
3164 \r
3165     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3166     holdpen = SelectObject( hdc, hpen );\r
3167     hbrush = CreateBrushIndirect( &stLB );\r
3168     holdbrush = SelectObject( hdc, hbrush );\r
3169 \r
3170     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3171 \r
3172     SelectObject( hdc, holdpen );\r
3173     SelectObject( hdc, holdbrush );\r
3174     DeleteObject( hpen );\r
3175     DeleteObject( hbrush );\r
3176 }\r
3177 \r
3178 BOOL HasHighlightInfo()\r
3179 {\r
3180     BOOL result = FALSE;\r
3181 \r
3182     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3183         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3184     {\r
3185         result = TRUE;\r
3186     }\r
3187 \r
3188     return result;\r
3189 }\r
3190 \r
3191 BOOL IsDrawArrowEnabled()\r
3192 {\r
3193     BOOL result = FALSE;\r
3194 \r
3195     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3196         result = TRUE;\r
3197     }\r
3198 \r
3199     return result;\r
3200 }\r
3201 \r
3202 VOID DrawArrowHighlight( HDC hdc )\r
3203 {\r
3204     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3205         DrawArrowBetweenSquares( hdc,\r
3206             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3207             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3208     }\r
3209 }\r
3210 \r
3211 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3212 {\r
3213     HRGN result = NULL;\r
3214 \r
3215     if( HasHighlightInfo() ) {\r
3216         int x1, y1, x2, y2;\r
3217         int sx, sy, dx, dy;\r
3218 \r
3219         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3220         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3221 \r
3222         sx = MIN( x1, x2 );\r
3223         sy = MIN( y1, y2 );\r
3224         dx = MAX( x1, x2 ) + squareSize;\r
3225         dy = MAX( y1, y2 ) + squareSize;\r
3226 \r
3227         result = CreateRectRgn( sx, sy, dx, dy );\r
3228     }\r
3229 \r
3230     return result;\r
3231 }\r
3232 \r
3233 /*\r
3234     Warning: this function modifies the behavior of several other functions. \r
3235     \r
3236     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3237     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3238     repaint is scattered all over the place, which is not good for features such as\r
3239     "arrow highlighting" that require a full repaint of the board.\r
3240 \r
3241     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3242     user interaction, when speed is not so important) but especially to avoid errors\r
3243     in the displayed graphics.\r
3244 \r
3245     In such patched places, I always try refer to this function so there is a single\r
3246     place to maintain knowledge.\r
3247     \r
3248     To restore the original behavior, just return FALSE unconditionally.\r
3249 */\r
3250 BOOL IsFullRepaintPreferrable()\r
3251 {\r
3252     BOOL result = FALSE;\r
3253 \r
3254     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3255         /* Arrow may appear on the board */\r
3256         result = TRUE;\r
3257     }\r
3258 \r
3259     return result;\r
3260 }\r
3261 \r
3262 /* \r
3263     This function is called by DrawPosition to know whether a full repaint must\r
3264     be forced or not.\r
3265 \r
3266     Only DrawPosition may directly call this function, which makes use of \r
3267     some state information. Other function should call DrawPosition specifying \r
3268     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3269 */\r
3270 BOOL DrawPositionNeedsFullRepaint()\r
3271 {\r
3272     BOOL result = FALSE;\r
3273 \r
3274     /* \r
3275         Probably a slightly better policy would be to trigger a full repaint\r
3276         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3277         but animation is fast enough that it's difficult to notice.\r
3278     */\r
3279     if( animInfo.piece == EmptySquare ) {\r
3280         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3281             result = TRUE;\r
3282         }\r
3283     }\r
3284 \r
3285     return result;\r
3286 }\r
3287 \r
3288 VOID\r
3289 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3290 {\r
3291   int row, column, x, y, square_color, piece_color;\r
3292   ChessSquare piece;\r
3293   HBRUSH oldBrush;\r
3294   HDC texture_hdc = NULL;\r
3295 \r
3296   /* [AS] Initialize background textures if needed */\r
3297   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3298       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3299       if( backTextureSquareSize != squareSize \r
3300        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3301           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3302           backTextureSquareSize = squareSize;\r
3303           RebuildTextureSquareInfo();\r
3304       }\r
3305 \r
3306       texture_hdc = CreateCompatibleDC( hdc );\r
3307   }\r
3308 \r
3309   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3310     for (column = 0; column < BOARD_WIDTH; column++) {\r
3311   \r
3312       SquareToPos(row, column, &x, &y);\r
3313 \r
3314       piece = board[row][column];\r
3315 \r
3316       square_color = ((column + row) % 2) == 1;\r
3317       if( gameInfo.variant == VariantXiangqi ) {\r
3318           square_color = !InPalace(row, column);\r
3319           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3320           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3321       }\r
3322       piece_color = (int) piece < (int) BlackPawn;\r
3323 \r
3324 \r
3325       /* [HGM] holdings file: light square or black */\r
3326       if(column == BOARD_LEFT-2) {\r
3327             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3328                 square_color = 1;\r
3329             else {\r
3330                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3331                 continue;\r
3332             }\r
3333       } else\r
3334       if(column == BOARD_RGHT + 1 ) {\r
3335             if( row < gameInfo.holdingsSize )\r
3336                 square_color = 1;\r
3337             else {\r
3338                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3339                 continue;\r
3340             }\r
3341       }\r
3342       if(column == BOARD_LEFT-1 ) /* left align */\r
3343             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3344       else if( column == BOARD_RGHT) /* right align */\r
3345             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3346       else\r
3347       if (appData.monoMode) {\r
3348         if (piece == EmptySquare) {\r
3349           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3350                  square_color ? WHITENESS : BLACKNESS);\r
3351         } else {\r
3352           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3353         }\r
3354       } \r
3355       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3356           /* [AS] Draw the square using a texture bitmap */\r
3357           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3358           int r = row, c = column; // [HGM] do not flip board in flipView\r
3359           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3360 \r
3361           DrawTile( x, y, \r
3362               squareSize, squareSize, \r
3363               hdc, \r
3364               texture_hdc,\r
3365               backTextureSquareInfo[r][c].mode,\r
3366               backTextureSquareInfo[r][c].x,\r
3367               backTextureSquareInfo[r][c].y );\r
3368 \r
3369           SelectObject( texture_hdc, hbm );\r
3370 \r
3371           if (piece != EmptySquare) {\r
3372               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3373           }\r
3374       }\r
3375       else {\r
3376         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3377 \r
3378         oldBrush = SelectObject(hdc, brush );\r
3379         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3380         SelectObject(hdc, oldBrush);\r
3381         if (piece != EmptySquare)\r
3382           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3383       }\r
3384     }\r
3385   }\r
3386 \r
3387   if( texture_hdc != NULL ) {\r
3388     DeleteDC( texture_hdc );\r
3389   }\r
3390 }\r
3391 \r
3392 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3393 void fputDW(FILE *f, int x)\r
3394 {\r
3395         fputc(x     & 255, f);\r
3396         fputc(x>>8  & 255, f);\r
3397         fputc(x>>16 & 255, f);\r
3398         fputc(x>>24 & 255, f);\r
3399 }\r
3400 \r
3401 #define MAX_CLIPS 200   /* more than enough */\r
3402 \r
3403 VOID\r
3404 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3405 {\r
3406 //  HBITMAP bufferBitmap;\r
3407   BITMAP bi;\r
3408 //  RECT Rect;\r
3409   HDC tmphdc;\r
3410   HBITMAP hbm;\r
3411   int w = 100, h = 50;\r
3412 \r
3413   if(logo == NULL) {\r
3414     if(!logoHeight) return;\r
3415     FillRect( hdc, &logoRect, whitePieceBrush );\r
3416   }\r
3417 //  GetClientRect(hwndMain, &Rect);\r
3418 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3419 //                                      Rect.bottom-Rect.top+1);\r
3420   tmphdc = CreateCompatibleDC(hdc);\r
3421   hbm = SelectObject(tmphdc, logo);\r
3422   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3423             w = bi.bmWidth;\r
3424             h = bi.bmHeight;\r
3425   }\r
3426   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3427                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3428   SelectObject(tmphdc, hbm);\r
3429   DeleteDC(tmphdc);\r
3430 }\r
3431 \r
3432 VOID\r
3433 DisplayLogos()\r
3434 {\r
3435   if(logoHeight) {\r
3436         HDC hdc = GetDC(hwndMain);\r
3437         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3438         if(appData.autoLogo) {\r
3439           \r
3440           switch(gameMode) { // pick logos based on game mode\r
3441             case IcsObserving:\r
3442                 whiteLogo = second.programLogo; // ICS logo\r
3443                 blackLogo = second.programLogo;\r
3444             default:\r
3445                 break;\r
3446             case IcsPlayingWhite:\r
3447                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3448                 blackLogo = second.programLogo; // ICS logo\r
3449                 break;\r
3450             case IcsPlayingBlack:\r
3451                 whiteLogo = second.programLogo; // ICS logo\r
3452                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3453                 break;\r
3454             case TwoMachinesPlay:\r
3455                 if(first.twoMachinesColor[0] == 'b') {\r
3456                     whiteLogo = second.programLogo;\r
3457                     blackLogo = first.programLogo;\r
3458                 }\r
3459                 break;\r
3460             case MachinePlaysWhite:\r
3461                 blackLogo = userLogo;\r
3462                 break;\r
3463             case MachinePlaysBlack:\r
3464                 whiteLogo = userLogo;\r
3465                 blackLogo = first.programLogo;\r
3466           }\r
3467         }\r
3468         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3469         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3470         ReleaseDC(hwndMain, hdc);\r
3471   }\r
3472 }\r
3473 \r
3474 void\r
3475 UpdateLogos(int display)\r
3476 { // called after loading new engine(s), in tourney or from menu\r
3477   LoadLogo(&first, 0, FALSE);\r
3478   LoadLogo(&second, 1, appData.icsActive);\r
3479   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3480   if(display) DisplayLogos();\r
3481 }\r
3482 \r
3483 static HDC hdcSeek;\r
3484 \r
3485 // [HGM] seekgraph\r
3486 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3487 {\r
3488     POINT stPt;\r
3489     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3490     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3491     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3492     SelectObject( hdcSeek, hp );\r
3493 }\r
3494 \r
3495 // front-end wrapper for drawing functions to do rectangles\r
3496 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3497 {\r
3498     HPEN hp;\r
3499     RECT rc;\r
3500 \r
3501     if (hdcSeek == NULL) {\r
3502     hdcSeek = GetDC(hwndMain);\r
3503       if (!appData.monoMode) {\r
3504         SelectPalette(hdcSeek, hPal, FALSE);\r
3505         RealizePalette(hdcSeek);\r
3506       }\r
3507     }\r
3508     hp = SelectObject( hdcSeek, gridPen );\r
3509     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3510     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3511     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3512     SelectObject( hdcSeek, hp );\r
3513 }\r
3514 \r
3515 // front-end wrapper for putting text in graph\r
3516 void DrawSeekText(char *buf, int x, int y)\r
3517 {\r
3518         SIZE stSize;\r
3519         SetBkMode( hdcSeek, TRANSPARENT );\r
3520         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3521         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3522 }\r
3523 \r
3524 void DrawSeekDot(int x, int y, int color)\r
3525 {\r
3526         int square = color & 0x80;\r
3527         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3528                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3529         color &= 0x7F;\r
3530         if(square)\r
3531             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3532                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3533         else\r
3534             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3535                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3536             SelectObject(hdcSeek, oldBrush);\r
3537 }\r
3538 \r
3539 VOID\r
3540 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3541 {\r
3542   static Board lastReq[2], lastDrawn[2];\r
3543   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3544   static int lastDrawnFlipView = 0;\r
3545   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3546   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3547   HDC tmphdc;\r
3548   HDC hdcmem;\r
3549   HBITMAP bufferBitmap;\r
3550   HBITMAP oldBitmap;\r
3551   RECT Rect;\r
3552   HRGN clips[MAX_CLIPS];\r
3553   ChessSquare dragged_piece = EmptySquare;\r
3554   int nr = twoBoards*partnerUp;\r
3555 \r
3556   /* I'm undecided on this - this function figures out whether a full\r
3557    * repaint is necessary on its own, so there's no real reason to have the\r
3558    * caller tell it that.  I think this can safely be set to FALSE - but\r
3559    * if we trust the callers not to request full repaints unnessesarily, then\r
3560    * we could skip some clipping work.  In other words, only request a full\r
3561    * redraw when the majority of pieces have changed positions (ie. flip, \r
3562    * gamestart and similar)  --Hawk\r
3563    */\r
3564   Boolean fullrepaint = repaint;\r
3565 \r
3566   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3567 \r
3568   if( DrawPositionNeedsFullRepaint() ) {\r
3569       fullrepaint = TRUE;\r
3570   }\r
3571 \r
3572   if (board == NULL) {\r
3573     if (!lastReqValid[nr]) {\r
3574       return;\r
3575     }\r
3576     board = lastReq[nr];\r
3577   } else {\r
3578     CopyBoard(lastReq[nr], board);\r
3579     lastReqValid[nr] = 1;\r
3580   }\r
3581 \r
3582   if (doingSizing) {\r
3583     return;\r
3584   }\r
3585 \r
3586   if (IsIconic(hwndMain)) {\r
3587     return;\r
3588   }\r
3589 \r
3590   if (hdc == NULL) {\r
3591     hdc = GetDC(hwndMain);\r
3592     if (!appData.monoMode) {\r
3593       SelectPalette(hdc, hPal, FALSE);\r
3594       RealizePalette(hdc);\r
3595     }\r
3596     releaseDC = TRUE;\r
3597   } else {\r
3598     releaseDC = FALSE;\r
3599   }\r
3600 \r
3601   /* Create some work-DCs */\r
3602   hdcmem = CreateCompatibleDC(hdc);\r
3603   tmphdc = CreateCompatibleDC(hdc);\r
3604 \r
3605   /* If dragging is in progress, we temporarely remove the piece */\r
3606   /* [HGM] or temporarily decrease count if stacked              */\r
3607   /*       !! Moved to before board compare !!                   */\r
3608   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3609     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3610     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3611             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3612         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3613     } else \r
3614     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3615             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3616         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3617     } else \r
3618         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3619   }\r
3620 \r
3621   /* Figure out which squares need updating by comparing the \r
3622    * newest board with the last drawn board and checking if\r
3623    * flipping has changed.\r
3624    */\r
3625   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3626     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3627       for (column = 0; column < BOARD_WIDTH; column++) {\r
3628         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3629           SquareToPos(row, column, &x, &y);\r
3630           clips[num_clips++] =\r
3631             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3632         }\r
3633       }\r
3634     }\r
3635    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3636     for (i=0; i<2; i++) {\r
3637       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3638           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3639         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3640             lastDrawnHighlight.sq[i].y >= 0) {\r
3641           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3642                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3643           clips[num_clips++] =\r
3644             CreateRectRgn(x - lineGap, y - lineGap, \r
3645                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3646         }\r
3647         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3648           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3649           clips[num_clips++] =\r
3650             CreateRectRgn(x - lineGap, y - lineGap, \r
3651                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3652         }\r
3653       }\r
3654     }\r
3655     for (i=0; i<2; i++) {\r
3656       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3657           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3658         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3659             lastDrawnPremove.sq[i].y >= 0) {\r
3660           SquareToPos(lastDrawnPremove.sq[i].y,\r
3661                       lastDrawnPremove.sq[i].x, &x, &y);\r
3662           clips[num_clips++] =\r
3663             CreateRectRgn(x - lineGap, y - lineGap, \r
3664                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3665         }\r
3666         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3667             premoveHighlightInfo.sq[i].y >= 0) {\r
3668           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3669                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3670           clips[num_clips++] =\r
3671             CreateRectRgn(x - lineGap, y - lineGap, \r
3672                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3673         }\r
3674       }\r
3675     }\r
3676    } else { // nr == 1\r
3677         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3678         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3679         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3680         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3681       for (i=0; i<2; i++) {\r
3682         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3683             partnerHighlightInfo.sq[i].y >= 0) {\r
3684           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3685                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3686           clips[num_clips++] =\r
3687             CreateRectRgn(x - lineGap, y - lineGap, \r
3688                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3689         }\r
3690         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3691             oldPartnerHighlight.sq[i].y >= 0) {\r
3692           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3693                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3694           clips[num_clips++] =\r
3695             CreateRectRgn(x - lineGap, y - lineGap, \r
3696                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3697         }\r
3698       }\r
3699    }\r
3700   } else {\r
3701     fullrepaint = TRUE;\r
3702   }\r
3703 \r
3704   /* Create a buffer bitmap - this is the actual bitmap\r
3705    * being written to.  When all the work is done, we can\r
3706    * copy it to the real DC (the screen).  This avoids\r
3707    * the problems with flickering.\r
3708    */\r
3709   GetClientRect(hwndMain, &Rect);\r
3710   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3711                                         Rect.bottom-Rect.top+1);\r
3712   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3713   if (!appData.monoMode) {\r
3714     SelectPalette(hdcmem, hPal, FALSE);\r
3715   }\r
3716 \r
3717   /* Create clips for dragging */\r
3718   if (!fullrepaint) {\r
3719     if (dragInfo.from.x >= 0) {\r
3720       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3721       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3722     }\r
3723     if (dragInfo.start.x >= 0) {\r
3724       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3725       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3726     }\r
3727     if (dragInfo.pos.x >= 0) {\r
3728       x = dragInfo.pos.x - squareSize / 2;\r
3729       y = dragInfo.pos.y - squareSize / 2;\r
3730       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3731     }\r
3732     if (dragInfo.lastpos.x >= 0) {\r
3733       x = dragInfo.lastpos.x - squareSize / 2;\r
3734       y = dragInfo.lastpos.y - squareSize / 2;\r
3735       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3736     }\r
3737   }\r
3738 \r
3739   /* Are we animating a move?  \r
3740    * If so, \r
3741    *   - remove the piece from the board (temporarely)\r
3742    *   - calculate the clipping region\r
3743    */\r
3744   if (!fullrepaint) {\r
3745     if (animInfo.piece != EmptySquare) {\r
3746       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3747       x = boardRect.left + animInfo.lastpos.x;\r
3748       y = boardRect.top + animInfo.lastpos.y;\r
3749       x2 = boardRect.left + animInfo.pos.x;\r
3750       y2 = boardRect.top + animInfo.pos.y;\r
3751       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3752       /* Slight kludge.  The real problem is that after AnimateMove is\r
3753          done, the position on the screen does not match lastDrawn.\r
3754          This currently causes trouble only on e.p. captures in\r
3755          atomic, where the piece moves to an empty square and then\r
3756          explodes.  The old and new positions both had an empty square\r
3757          at the destination, but animation has drawn a piece there and\r
3758          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3759       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3760     }\r
3761   }\r
3762 \r
3763   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3764   if (num_clips == 0)\r
3765     fullrepaint = TRUE;\r
3766 \r
3767   /* Set clipping on the memory DC */\r
3768   if (!fullrepaint) {\r
3769     SelectClipRgn(hdcmem, clips[0]);\r
3770     for (x = 1; x < num_clips; x++) {\r
3771       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3772         abort();  // this should never ever happen!\r
3773     }\r
3774   }\r
3775 \r
3776   /* Do all the drawing to the memory DC */\r
3777   if(explodeInfo.radius) { // [HGM] atomic\r
3778         HBRUSH oldBrush;\r
3779         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3780         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3781         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3782         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3783         x += squareSize/2;\r
3784         y += squareSize/2;\r
3785         if(!fullrepaint) {\r
3786           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3787           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3788         }\r
3789         DrawGridOnDC(hdcmem);\r
3790         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3791         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3792         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3793         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3794         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3795         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3796         SelectObject(hdcmem, oldBrush);\r
3797   } else {\r
3798     DrawGridOnDC(hdcmem);\r
3799     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3800         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3801         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3802     } else {\r
3803         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3804         oldPartnerHighlight = partnerHighlightInfo;\r
3805     }\r
3806     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3807   }\r
3808   if(nr == 0) // [HGM] dual: markers only on left board\r
3809   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3810     for (column = 0; column < BOARD_WIDTH; column++) {\r
3811         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3812             HBRUSH oldBrush = SelectObject(hdcmem, \r
3813                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3814             SquareToPos(row, column, &x, &y);\r
3815             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3816                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3817             SelectObject(hdcmem, oldBrush);\r
3818         }\r
3819     }\r
3820   }\r
3821 \r
3822   if( appData.highlightMoveWithArrow ) {\r
3823     DrawArrowHighlight(hdcmem);\r
3824   }\r
3825 \r
3826   DrawCoordsOnDC(hdcmem);\r
3827 \r
3828   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3829                  /* to make sure lastDrawn contains what is actually drawn */\r
3830 \r
3831   /* Put the dragged piece back into place and draw it (out of place!) */\r
3832     if (dragged_piece != EmptySquare) {\r
3833     /* [HGM] or restack */\r
3834     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3835                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3836     else\r
3837     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3838                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3839     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3840     x = dragInfo.pos.x - squareSize / 2;\r
3841     y = dragInfo.pos.y - squareSize / 2;\r
3842     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3843                   ((int) dragInfo.piece < (int) BlackPawn), \r
3844                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3845   }   \r
3846   \r
3847   /* Put the animated piece back into place and draw it */\r
3848   if (animInfo.piece != EmptySquare) {\r
3849     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3850     x = boardRect.left + animInfo.pos.x;\r
3851     y = boardRect.top + animInfo.pos.y;\r
3852     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3853                   ((int) animInfo.piece < (int) BlackPawn),\r
3854                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3855   }\r
3856 \r
3857   /* Release the bufferBitmap by selecting in the old bitmap \r
3858    * and delete the memory DC\r
3859    */\r
3860   SelectObject(hdcmem, oldBitmap);\r
3861   DeleteDC(hdcmem);\r
3862 \r
3863   /* Set clipping on the target DC */\r
3864   if (!fullrepaint) {\r
3865     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3866         RECT rect;\r
3867         GetRgnBox(clips[x], &rect);\r
3868         DeleteObject(clips[x]);\r
3869         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3870                           rect.right + wpMain.width/2, rect.bottom);\r
3871     }\r
3872     SelectClipRgn(hdc, clips[0]);\r
3873     for (x = 1; x < num_clips; x++) {\r
3874       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3875         abort();   // this should never ever happen!\r
3876     } \r
3877   }\r
3878 \r
3879   /* Copy the new bitmap onto the screen in one go.\r
3880    * This way we avoid any flickering\r
3881    */\r
3882   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3883   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3884          boardRect.right - boardRect.left,\r
3885          boardRect.bottom - boardRect.top,\r
3886          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3887   if(saveDiagFlag) { \r
3888     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3889     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3890 \r
3891     GetObject(bufferBitmap, sizeof(b), &b);\r
3892     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3893         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3894         bih.biWidth = b.bmWidth;\r
3895         bih.biHeight = b.bmHeight;\r
3896         bih.biPlanes = 1;\r
3897         bih.biBitCount = b.bmBitsPixel;\r
3898         bih.biCompression = 0;\r
3899         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3900         bih.biXPelsPerMeter = 0;\r
3901         bih.biYPelsPerMeter = 0;\r
3902         bih.biClrUsed = 0;\r
3903         bih.biClrImportant = 0;\r
3904 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3905 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3906         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3907 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3908 \r
3909         wb = b.bmWidthBytes;\r
3910         // count colors\r
3911         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3912                 int k = ((int*) pData)[i];\r
3913                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3914                 if(j >= 16) break;\r
3915                 color[j] = k;\r
3916                 if(j >= nrColors) nrColors = j+1;\r
3917         }\r
3918         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3919                 INT p = 0;\r
3920                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3921                     for(w=0; w<(wb>>2); w+=2) {\r
3922                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3923                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3924                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3925                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3926                         pData[p++] = m | j<<4;\r
3927                     }\r
3928                     while(p&3) pData[p++] = 0;\r
3929                 }\r
3930                 fac = 3;\r
3931                 wb = ((wb+31)>>5)<<2;\r
3932         }\r
3933         // write BITMAPFILEHEADER\r
3934         fprintf(diagFile, "BM");\r
3935         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3936         fputDW(diagFile, 0);\r
3937         fputDW(diagFile, 0x36 + (fac?64:0));\r
3938         // write BITMAPINFOHEADER\r
3939         fputDW(diagFile, 40);\r
3940         fputDW(diagFile, b.bmWidth);\r
3941         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3942         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3943         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3944         fputDW(diagFile, 0);\r
3945         fputDW(diagFile, 0);\r
3946         fputDW(diagFile, 0);\r
3947         fputDW(diagFile, 0);\r
3948         fputDW(diagFile, 0);\r
3949         fputDW(diagFile, 0);\r
3950         // write color table\r
3951         if(fac)\r
3952         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3953         // write bitmap data\r
3954         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3955                 fputc(pData[i], diagFile);\r
3956         free(pData);\r
3957      }\r
3958   }\r
3959 \r
3960   SelectObject(tmphdc, oldBitmap);\r
3961 \r
3962   /* Massive cleanup */\r
3963   for (x = 0; x < num_clips; x++)\r
3964     DeleteObject(clips[x]);\r
3965 \r
3966   DeleteDC(tmphdc);\r
3967   DeleteObject(bufferBitmap);\r
3968 \r
3969   if (releaseDC) \r
3970     ReleaseDC(hwndMain, hdc);\r
3971   \r
3972   if (lastDrawnFlipView != flipView && nr == 0) {\r
3973     if (flipView)\r
3974       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3975     else\r
3976       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3977   }\r
3978 \r
3979 /*  CopyBoard(lastDrawn, board);*/\r
3980   lastDrawnHighlight = highlightInfo;\r
3981   lastDrawnPremove   = premoveHighlightInfo;\r
3982   lastDrawnFlipView = flipView;\r
3983   lastDrawnValid[nr] = 1;\r
3984 }\r
3985 \r
3986 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3987 int\r
3988 SaveDiagram(f)\r
3989      FILE *f;\r
3990 {\r
3991     saveDiagFlag = 1; diagFile = f;\r
3992     HDCDrawPosition(NULL, TRUE, NULL);\r
3993     saveDiagFlag = 0;\r
3994 \r
3995     fclose(f);\r
3996     return TRUE;\r
3997 }\r
3998 \r
3999 \r
4000 /*---------------------------------------------------------------------------*\\r
4001 | CLIENT PAINT PROCEDURE\r
4002 |   This is the main event-handler for the WM_PAINT message.\r
4003 |\r
4004 \*---------------------------------------------------------------------------*/\r
4005 VOID\r
4006 PaintProc(HWND hwnd)\r
4007 {\r
4008   HDC         hdc;\r
4009   PAINTSTRUCT ps;\r
4010   HFONT       oldFont;\r
4011 \r
4012   if((hdc = BeginPaint(hwnd, &ps))) {\r
4013     if (IsIconic(hwnd)) {\r
4014       DrawIcon(hdc, 2, 2, iconCurrent);\r
4015     } else {\r
4016       if (!appData.monoMode) {\r
4017         SelectPalette(hdc, hPal, FALSE);\r
4018         RealizePalette(hdc);\r
4019       }\r
4020       HDCDrawPosition(hdc, 1, NULL);\r
4021       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4022         flipView = !flipView; partnerUp = !partnerUp;\r
4023         HDCDrawPosition(hdc, 1, NULL);\r
4024         flipView = !flipView; partnerUp = !partnerUp;\r
4025       }\r
4026       oldFont =\r
4027         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4028       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4029                  ETO_CLIPPED|ETO_OPAQUE,\r
4030                  &messageRect, messageText, strlen(messageText), NULL);\r
4031       SelectObject(hdc, oldFont);\r
4032       DisplayBothClocks();\r
4033       DisplayLogos();\r
4034     }\r
4035     EndPaint(hwnd,&ps);\r
4036   }\r
4037 \r
4038   return;\r
4039 }\r
4040 \r
4041 \r
4042 /*\r
4043  * If the user selects on a border boundary, return -1; if off the board,\r
4044  *   return -2.  Otherwise map the event coordinate to the square.\r
4045  * The offset boardRect.left or boardRect.top must already have been\r
4046  *   subtracted from x.\r
4047  */\r
4048 int EventToSquare(x, limit)\r
4049      int x, limit;\r
4050 {\r
4051   if (x <= 0)\r
4052     return -2;\r
4053   if (x < lineGap)\r
4054     return -1;\r
4055   x -= lineGap;\r
4056   if ((x % (squareSize + lineGap)) >= squareSize)\r
4057     return -1;\r
4058   x /= (squareSize + lineGap);\r
4059     if (x >= limit)\r
4060     return -2;\r
4061   return x;\r
4062 }\r
4063 \r
4064 typedef struct {\r
4065   char piece;\r
4066   int command;\r
4067   char* name;\r
4068 } DropEnable;\r
4069 \r
4070 DropEnable dropEnables[] = {\r
4071   { 'P', DP_Pawn, N_("Pawn") },\r
4072   { 'N', DP_Knight, N_("Knight") },\r
4073   { 'B', DP_Bishop, N_("Bishop") },\r
4074   { 'R', DP_Rook, N_("Rook") },\r
4075   { 'Q', DP_Queen, N_("Queen") },\r
4076 };\r
4077 \r
4078 VOID\r
4079 SetupDropMenu(HMENU hmenu)\r
4080 {\r
4081   int i, count, enable;\r
4082   char *p;\r
4083   extern char white_holding[], black_holding[];\r
4084   char item[MSG_SIZ];\r
4085 \r
4086   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4087     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4088                dropEnables[i].piece);\r
4089     count = 0;\r
4090     while (p && *p++ == dropEnables[i].piece) count++;\r
4091       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4092     enable = count > 0 || !appData.testLegality\r
4093       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4094                       && !appData.icsActive);\r
4095     ModifyMenu(hmenu, dropEnables[i].command,\r
4096                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4097                dropEnables[i].command, item);\r
4098   }\r
4099 }\r
4100 \r
4101 void DragPieceBegin(int x, int y, Boolean instantly)\r
4102 {\r
4103       dragInfo.lastpos.x = boardRect.left + x;\r
4104       dragInfo.lastpos.y = boardRect.top + y;\r
4105       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4106       dragInfo.from.x = fromX;\r
4107       dragInfo.from.y = fromY;\r
4108       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4109       dragInfo.start = dragInfo.from;\r
4110       SetCapture(hwndMain);\r
4111 }\r
4112 \r
4113 void DragPieceEnd(int x, int y)\r
4114 {\r
4115     ReleaseCapture();\r
4116     dragInfo.start.x = dragInfo.start.y = -1;\r
4117     dragInfo.from = dragInfo.start;\r
4118     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4119 }\r
4120 \r
4121 void ChangeDragPiece(ChessSquare piece)\r
4122 {\r
4123     dragInfo.piece = piece;\r
4124 }\r
4125 \r
4126 /* Event handler for mouse messages */\r
4127 VOID\r
4128 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4129 {\r
4130   int x, y, menuNr;\r
4131   POINT pt;\r
4132   static int recursive = 0;\r
4133   HMENU hmenu;\r
4134   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4135 \r
4136   if (recursive) {\r
4137     if (message == WM_MBUTTONUP) {\r
4138       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4139          to the middle button: we simulate pressing the left button too!\r
4140          */\r
4141       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4142       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4143     }\r
4144     return;\r
4145   }\r
4146   recursive++;\r
4147   \r
4148   pt.x = LOWORD(lParam);\r
4149   pt.y = HIWORD(lParam);\r
4150   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4151   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4152   if (!flipView && y >= 0) {\r
4153     y = BOARD_HEIGHT - 1 - y;\r
4154   }\r
4155   if (flipView && x >= 0) {\r
4156     x = BOARD_WIDTH - 1 - x;\r
4157   }\r
4158 \r
4159   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4160 \r
4161   switch (message) {\r
4162   case WM_LBUTTONDOWN:\r
4163       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4164         ClockClick(flipClock); break;\r
4165       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4166         ClockClick(!flipClock); break;\r
4167       }\r
4168       dragInfo.start.x = dragInfo.start.y = -1;\r
4169       dragInfo.from = dragInfo.start;\r
4170     if(fromX == -1 && frozen) { // not sure where this is for\r
4171                 fromX = fromY = -1; \r
4172       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4173       break;\r
4174     }\r
4175       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4176       DrawPosition(TRUE, NULL);\r
4177     break;\r
4178 \r
4179   case WM_LBUTTONUP:\r
4180       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4181       DrawPosition(TRUE, NULL);\r
4182     break;\r
4183 \r
4184   case WM_MOUSEMOVE:\r
4185     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4186     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4187     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4188     if ((appData.animateDragging || appData.highlightDragging)\r
4189         && (wParam & MK_LBUTTON)\r
4190         && dragInfo.from.x >= 0) \r
4191     {\r
4192       BOOL full_repaint = FALSE;\r
4193 \r
4194       if (appData.animateDragging) {\r
4195         dragInfo.pos = pt;\r
4196       }\r
4197       if (appData.highlightDragging) {\r
4198         SetHighlights(fromX, fromY, x, y);\r
4199         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4200             full_repaint = TRUE;\r
4201         }\r
4202       }\r
4203       \r
4204       DrawPosition( full_repaint, NULL);\r
4205       \r
4206       dragInfo.lastpos = dragInfo.pos;\r
4207     }\r
4208     break;\r
4209 \r
4210   case WM_MOUSEWHEEL: // [DM]\r
4211     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4212        /* Mouse Wheel is being rolled forward\r
4213         * Play moves forward\r
4214         */\r
4215        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4216                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4217        /* Mouse Wheel is being rolled backward\r
4218         * Play moves backward\r
4219         */\r
4220        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4221                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4222     }\r
4223     break;\r
4224 \r
4225   case WM_MBUTTONUP:\r
4226   case WM_RBUTTONUP:\r
4227     ReleaseCapture();\r
4228     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4229     break;\r
4230  \r
4231   case WM_MBUTTONDOWN:\r
4232   case WM_RBUTTONDOWN:\r
4233     ErrorPopDown();\r
4234     ReleaseCapture();\r
4235     fromX = fromY = -1;\r
4236     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4237     dragInfo.start.x = dragInfo.start.y = -1;\r
4238     dragInfo.from = dragInfo.start;\r
4239     dragInfo.lastpos = dragInfo.pos;\r
4240     if (appData.highlightDragging) {\r
4241       ClearHighlights();\r
4242     }\r
4243     if(y == -2) {\r
4244       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4245       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4246           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4247       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4248           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4249       }\r
4250       break;\r
4251     }\r
4252     DrawPosition(TRUE, NULL);\r
4253 \r
4254     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4255     switch (menuNr) {\r
4256     case 0:\r
4257       if (message == WM_MBUTTONDOWN) {\r
4258         buttonCount = 3;  /* even if system didn't think so */\r
4259         if (wParam & MK_SHIFT) \r
4260           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4261         else\r
4262           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4263       } else { /* message == WM_RBUTTONDOWN */\r
4264         /* Just have one menu, on the right button.  Windows users don't\r
4265            think to try the middle one, and sometimes other software steals\r
4266            it, or it doesn't really exist. */\r
4267         if(gameInfo.variant != VariantShogi)\r
4268             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4269         else\r
4270             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4271       }\r
4272       break;\r
4273     case 2:\r
4274       SetCapture(hwndMain);\r
4275       break;\r
4276     case 1:\r
4277       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4278       SetupDropMenu(hmenu);\r
4279       MenuPopup(hwnd, pt, hmenu, -1);\r
4280     default:\r
4281       break;\r
4282     }\r
4283     break;\r
4284   }\r
4285 \r
4286   recursive--;\r
4287 }\r
4288 \r
4289 /* Preprocess messages for buttons in main window */\r
4290 LRESULT CALLBACK\r
4291 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4292 {\r
4293   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4294   int i, dir;\r
4295 \r
4296   for (i=0; i<N_BUTTONS; i++) {\r
4297     if (buttonDesc[i].id == id) break;\r
4298   }\r
4299   if (i == N_BUTTONS) return 0;\r
4300   switch (message) {\r
4301   case WM_KEYDOWN:\r
4302     switch (wParam) {\r
4303     case VK_LEFT:\r
4304     case VK_RIGHT:\r
4305       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4306       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4307       return TRUE;\r
4308     }\r
4309     break;\r
4310   case WM_CHAR:\r
4311     switch (wParam) {\r
4312     case '\r':\r
4313       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4314       return TRUE;\r
4315     default:\r
4316       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4317         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4318         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4319         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4320         SetFocus(h);\r
4321         SendMessage(h, WM_CHAR, wParam, lParam);\r
4322         return TRUE;\r
4323       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4324         TypeInEvent((char)wParam);\r
4325       }\r
4326       break;\r
4327     }\r
4328     break;\r
4329   }\r
4330   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4331 }\r
4332 \r
4333 /* Process messages for Promotion dialog box */\r
4334 LRESULT CALLBACK\r
4335 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4336 {\r
4337   char promoChar;\r
4338 \r
4339   switch (message) {\r
4340   case WM_INITDIALOG: /* message: initialize dialog box */\r
4341     /* Center the dialog over the application window */\r
4342     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4343     Translate(hDlg, DLG_PromotionKing);\r
4344     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4345       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4346        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4347        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4348                SW_SHOW : SW_HIDE);\r
4349     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4350     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4351        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4352          PieceToChar(WhiteAngel) != '~') ||\r
4353         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4354          PieceToChar(BlackAngel) != '~')   ) ?\r
4355                SW_SHOW : SW_HIDE);\r
4356     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4357        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4358          PieceToChar(WhiteMarshall) != '~') ||\r
4359         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4360          PieceToChar(BlackMarshall) != '~')   ) ?\r
4361                SW_SHOW : SW_HIDE);\r
4362     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4363     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4364        gameInfo.variant != VariantShogi ?\r
4365                SW_SHOW : SW_HIDE);\r
4366     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4367        gameInfo.variant != VariantShogi ?\r
4368                SW_SHOW : SW_HIDE);\r
4369     if(gameInfo.variant == VariantShogi) {\r
4370         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4371         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4372         SetWindowText(hDlg, "Promote?");\r
4373     }\r
4374     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4375        gameInfo.variant == VariantSuper ?\r
4376                SW_SHOW : SW_HIDE);\r
4377     return TRUE;\r
4378 \r
4379   case WM_COMMAND: /* message: received a command */\r
4380     switch (LOWORD(wParam)) {\r
4381     case IDCANCEL:\r
4382       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4383       ClearHighlights();\r
4384       DrawPosition(FALSE, NULL);\r
4385       return TRUE;\r
4386     case PB_King:\r
4387       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4388       break;\r
4389     case PB_Queen:\r
4390       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4391       break;\r
4392     case PB_Rook:\r
4393       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4394       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4395       break;\r
4396     case PB_Bishop:\r
4397       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4398       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4399       break;\r
4400     case PB_Chancellor:\r
4401       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4402       break;\r
4403     case PB_Archbishop:\r
4404       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4405       break;\r
4406     case PB_Knight:\r
4407       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4408       break;\r
4409     default:\r
4410       return FALSE;\r
4411     }\r
4412     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4413     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4414     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4415     fromX = fromY = -1;\r
4416     if (!appData.highlightLastMove) {\r
4417       ClearHighlights();\r
4418       DrawPosition(FALSE, NULL);\r
4419     }\r
4420     return TRUE;\r
4421   }\r
4422   return FALSE;\r
4423 }\r
4424 \r
4425 /* Pop up promotion dialog */\r
4426 VOID\r
4427 PromotionPopup(HWND hwnd)\r
4428 {\r
4429   FARPROC lpProc;\r
4430 \r
4431   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4432   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4433     hwnd, (DLGPROC)lpProc);\r
4434   FreeProcInstance(lpProc);\r
4435 }\r
4436 \r
4437 void\r
4438 PromotionPopUp()\r
4439 {\r
4440   DrawPosition(TRUE, NULL);\r
4441   PromotionPopup(hwndMain);\r
4442 }\r
4443 \r
4444 /* Toggle ShowThinking */\r
4445 VOID\r
4446 ToggleShowThinking()\r
4447 {\r
4448   appData.showThinking = !appData.showThinking;\r
4449   ShowThinkingEvent();\r
4450 }\r
4451 \r
4452 VOID\r
4453 LoadGameDialog(HWND hwnd, char* title)\r
4454 {\r
4455   UINT number = 0;\r
4456   FILE *f;\r
4457   char fileTitle[MSG_SIZ];\r
4458   f = OpenFileDialog(hwnd, "rb", "",\r
4459                      appData.oldSaveStyle ? "gam" : "pgn",\r
4460                      GAME_FILT,\r
4461                      title, &number, fileTitle, NULL);\r
4462   if (f != NULL) {\r
4463     cmailMsgLoaded = FALSE;\r
4464     if (number == 0) {\r
4465       int error = GameListBuild(f);\r
4466       if (error) {\r
4467         DisplayError(_("Cannot build game list"), error);\r
4468       } else if (!ListEmpty(&gameList) &&\r
4469                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4470         GameListPopUp(f, fileTitle);\r
4471         return;\r
4472       }\r
4473       GameListDestroy();\r
4474       number = 1;\r
4475     }\r
4476     LoadGame(f, number, fileTitle, FALSE);\r
4477   }\r
4478 }\r
4479 \r
4480 int get_term_width()\r
4481 {\r
4482     HDC hdc;\r
4483     TEXTMETRIC tm;\r
4484     RECT rc;\r
4485     HFONT hfont, hold_font;\r
4486     LOGFONT lf;\r
4487     HWND hText;\r
4488 \r
4489     if (hwndConsole)\r
4490         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4491     else\r
4492         return 79;\r
4493 \r
4494     // get the text metrics\r
4495     hdc = GetDC(hText);\r
4496     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4497     if (consoleCF.dwEffects & CFE_BOLD)\r
4498         lf.lfWeight = FW_BOLD;\r
4499     if (consoleCF.dwEffects & CFE_ITALIC)\r
4500         lf.lfItalic = TRUE;\r
4501     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4502         lf.lfStrikeOut = TRUE;\r
4503     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4504         lf.lfUnderline = TRUE;\r
4505     hfont = CreateFontIndirect(&lf);\r
4506     hold_font = SelectObject(hdc, hfont);\r
4507     GetTextMetrics(hdc, &tm);\r
4508     SelectObject(hdc, hold_font);\r
4509     DeleteObject(hfont);\r
4510     ReleaseDC(hText, hdc);\r
4511 \r
4512     // get the rectangle\r
4513     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4514 \r
4515     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4516 }\r
4517 \r
4518 void UpdateICSWidth(HWND hText)\r
4519 {\r
4520     LONG old_width, new_width;\r
4521 \r
4522     new_width = get_term_width(hText, FALSE);\r
4523     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4524     if (new_width != old_width)\r
4525     {\r
4526         ics_update_width(new_width);\r
4527         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4528     }\r
4529 }\r
4530 \r
4531 VOID\r
4532 ChangedConsoleFont()\r
4533 {\r
4534   CHARFORMAT cfmt;\r
4535   CHARRANGE tmpsel, sel;\r
4536   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4537   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4538   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4539   PARAFORMAT paraf;\r
4540 \r
4541   cfmt.cbSize = sizeof(CHARFORMAT);\r
4542   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4543     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4544                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4545   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4546    * size.  This was undocumented in the version of MSVC++ that I had\r
4547    * when I wrote the code, but is apparently documented now.\r
4548    */\r
4549   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4550   cfmt.bCharSet = f->lf.lfCharSet;\r
4551   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4552   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4553   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4554   /* Why are the following seemingly needed too? */\r
4555   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4556   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4557   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4558   tmpsel.cpMin = 0;\r
4559   tmpsel.cpMax = -1; /*999999?*/\r
4560   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4561   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4562   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4563    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4564    */\r
4565   paraf.cbSize = sizeof(paraf);\r
4566   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4567   paraf.dxStartIndent = 0;\r
4568   paraf.dxOffset = WRAP_INDENT;\r
4569   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4570   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4571   UpdateICSWidth(hText);\r
4572 }\r
4573 \r
4574 /*---------------------------------------------------------------------------*\\r
4575  *\r
4576  * Window Proc for main window\r
4577  *\r
4578 \*---------------------------------------------------------------------------*/\r
4579 \r
4580 /* Process messages for main window, etc. */\r
4581 LRESULT CALLBACK\r
4582 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4583 {\r
4584   FARPROC lpProc;\r
4585   int wmId, wmEvent;\r
4586   char *defName;\r
4587   FILE *f;\r
4588   UINT number;\r
4589   char fileTitle[MSG_SIZ];\r
4590   char buf[MSG_SIZ];\r
4591   static SnapData sd;\r
4592   static int peek=0;\r
4593 \r
4594   switch (message) {\r
4595 \r
4596   case WM_PAINT: /* message: repaint portion of window */\r
4597     PaintProc(hwnd);\r
4598     break;\r
4599 \r
4600   case WM_ERASEBKGND:\r
4601     if (IsIconic(hwnd)) {\r
4602       /* Cheat; change the message */\r
4603       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4604     } else {\r
4605       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4606     }\r
4607     break;\r
4608 \r
4609   case WM_LBUTTONDOWN:\r
4610   case WM_MBUTTONDOWN:\r
4611   case WM_RBUTTONDOWN:\r
4612   case WM_LBUTTONUP:\r
4613   case WM_MBUTTONUP:\r
4614   case WM_RBUTTONUP:\r
4615   case WM_MOUSEMOVE:\r
4616   case WM_MOUSEWHEEL:\r
4617     MouseEvent(hwnd, message, wParam, lParam);\r
4618     break;\r
4619 \r
4620   case WM_KEYUP:\r
4621     if((char)wParam == '\b') {\r
4622       ForwardEvent(); peek = 0;\r
4623     }\r
4624 \r
4625     JAWS_KBUP_NAVIGATION\r
4626 \r
4627     break;\r
4628 \r
4629   case WM_KEYDOWN:\r
4630     if((char)wParam == '\b') {\r
4631       if(!peek) BackwardEvent(), peek = 1;\r
4632     }\r
4633 \r
4634     JAWS_KBDOWN_NAVIGATION\r
4635 \r
4636     break;\r
4637 \r
4638   case WM_CHAR:\r
4639     \r
4640     JAWS_ALT_INTERCEPT\r
4641 \r
4642     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4643         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4644         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4645         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4646         SetFocus(h);\r
4647         SendMessage(h, message, wParam, lParam);\r
4648     } else if(lParam != KF_REPEAT) {\r
4649         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4650                 TypeInEvent((char)wParam);\r
4651         } else if((char)wParam == 003) CopyGameToClipboard();\r
4652          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4653     }\r
4654 \r
4655     break;\r
4656 \r
4657   case WM_PALETTECHANGED:\r
4658     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4659       int nnew;\r
4660       HDC hdc = GetDC(hwndMain);\r
4661       SelectPalette(hdc, hPal, TRUE);\r
4662       nnew = RealizePalette(hdc);\r
4663       if (nnew > 0) {\r
4664         paletteChanged = TRUE;\r
4665         InvalidateRect(hwnd, &boardRect, FALSE);\r
4666       }\r
4667       ReleaseDC(hwnd, hdc);\r
4668     }\r
4669     break;\r
4670 \r
4671   case WM_QUERYNEWPALETTE:\r
4672     if (!appData.monoMode /*&& paletteChanged*/) {\r
4673       int nnew;\r
4674       HDC hdc = GetDC(hwndMain);\r
4675       paletteChanged = FALSE;\r
4676       SelectPalette(hdc, hPal, FALSE);\r
4677       nnew = RealizePalette(hdc);\r
4678       if (nnew > 0) {\r
4679         InvalidateRect(hwnd, &boardRect, FALSE);\r
4680       }\r
4681       ReleaseDC(hwnd, hdc);\r
4682       return TRUE;\r
4683     }\r
4684     return FALSE;\r
4685 \r
4686   case WM_COMMAND: /* message: command from application menu */\r
4687     wmId    = LOWORD(wParam);\r
4688     wmEvent = HIWORD(wParam);\r
4689 \r
4690     switch (wmId) {\r
4691     case IDM_NewGame:\r
4692       ResetGameEvent();\r
4693       SAY("new game enter a move to play against the computer with white");\r
4694       break;\r
4695 \r
4696     case IDM_NewGameFRC:\r
4697       if( NewGameFRC() == 0 ) {\r
4698         ResetGameEvent();\r
4699       }\r
4700       break;\r
4701 \r
4702     case IDM_NewVariant:\r
4703       NewVariantPopup(hwnd);\r
4704       break;\r
4705 \r
4706     case IDM_LoadGame:\r
4707       LoadGameDialog(hwnd, _("Load Game from File"));\r
4708       break;\r
4709 \r
4710     case IDM_LoadNextGame:\r
4711       ReloadGame(1);\r
4712       break;\r
4713 \r
4714     case IDM_LoadPrevGame:\r
4715       ReloadGame(-1);\r
4716       break;\r
4717 \r
4718     case IDM_ReloadGame:\r
4719       ReloadGame(0);\r
4720       break;\r
4721 \r
4722     case IDM_LoadPosition:\r
4723       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4724         Reset(FALSE, TRUE);\r
4725       }\r
4726       number = 1;\r
4727       f = OpenFileDialog(hwnd, "rb", "",\r
4728                          appData.oldSaveStyle ? "pos" : "fen",\r
4729                          POSITION_FILT,\r
4730                          _("Load Position from File"), &number, fileTitle, NULL);\r
4731       if (f != NULL) {\r
4732         LoadPosition(f, number, fileTitle);\r
4733       }\r
4734       break;\r
4735 \r
4736     case IDM_LoadNextPosition:\r
4737       ReloadPosition(1);\r
4738       break;\r
4739 \r
4740     case IDM_LoadPrevPosition:\r
4741       ReloadPosition(-1);\r
4742       break;\r
4743 \r
4744     case IDM_ReloadPosition:\r
4745       ReloadPosition(0);\r
4746       break;\r
4747 \r
4748     case IDM_SaveGame:\r
4749       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4750       f = OpenFileDialog(hwnd, "a", defName,\r
4751                          appData.oldSaveStyle ? "gam" : "pgn",\r
4752                          GAME_FILT,\r
4753                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4754       if (f != NULL) {\r
4755         SaveGame(f, 0, "");\r
4756       }\r
4757       break;\r
4758 \r
4759     case IDM_SavePosition:\r
4760       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4761       f = OpenFileDialog(hwnd, "a", defName,\r
4762                          appData.oldSaveStyle ? "pos" : "fen",\r
4763                          POSITION_FILT,\r
4764                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4765       if (f != NULL) {\r
4766         SavePosition(f, 0, "");\r
4767       }\r
4768       break;\r
4769 \r
4770     case IDM_SaveDiagram:\r
4771       defName = "diagram";\r
4772       f = OpenFileDialog(hwnd, "wb", defName,\r
4773                          "bmp",\r
4774                          DIAGRAM_FILT,\r
4775                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4776       if (f != NULL) {\r
4777         SaveDiagram(f);\r
4778       }\r
4779       break;\r
4780 \r
4781     case IDM_CopyGame:\r
4782       CopyGameToClipboard();\r
4783       break;\r
4784 \r
4785     case IDM_PasteGame:\r
4786       PasteGameFromClipboard();\r
4787       break;\r
4788 \r
4789     case IDM_CopyGameListToClipboard:\r
4790       CopyGameListToClipboard();\r
4791       break;\r
4792 \r
4793     /* [AS] Autodetect FEN or PGN data */\r
4794     case IDM_PasteAny:\r
4795       PasteGameOrFENFromClipboard();\r
4796       break;\r
4797 \r
4798     /* [AS] Move history */\r
4799     case IDM_ShowMoveHistory:\r
4800         if( MoveHistoryIsUp() ) {\r
4801             MoveHistoryPopDown();\r
4802         }\r
4803         else {\r
4804             MoveHistoryPopUp();\r
4805         }\r
4806         break;\r
4807 \r
4808     /* [AS] Eval graph */\r
4809     case IDM_ShowEvalGraph:\r
4810         if( EvalGraphIsUp() ) {\r
4811             EvalGraphPopDown();\r
4812         }\r
4813         else {\r
4814             EvalGraphPopUp();\r
4815             SetFocus(hwndMain);\r
4816         }\r
4817         break;\r
4818 \r
4819     /* [AS] Engine output */\r
4820     case IDM_ShowEngineOutput:\r
4821         if( EngineOutputIsUp() ) {\r
4822             EngineOutputPopDown();\r
4823         }\r
4824         else {\r
4825             EngineOutputPopUp();\r
4826         }\r
4827         break;\r
4828 \r
4829     /* [AS] User adjudication */\r
4830     case IDM_UserAdjudication_White:\r
4831         UserAdjudicationEvent( +1 );\r
4832         break;\r
4833 \r
4834     case IDM_UserAdjudication_Black:\r
4835         UserAdjudicationEvent( -1 );\r
4836         break;\r
4837 \r
4838     case IDM_UserAdjudication_Draw:\r
4839         UserAdjudicationEvent( 0 );\r
4840         break;\r
4841 \r
4842     /* [AS] Game list options dialog */\r
4843     case IDM_GameListOptions:\r
4844       GameListOptions();\r
4845       break;\r
4846 \r
4847     case IDM_NewChat:\r
4848       ChatPopUp(NULL);\r
4849       break;\r
4850 \r
4851     case IDM_CopyPosition:\r
4852       CopyFENToClipboard();\r
4853       break;\r
4854 \r
4855     case IDM_PastePosition:\r
4856       PasteFENFromClipboard();\r
4857       break;\r
4858 \r
4859     case IDM_MailMove:\r
4860       MailMoveEvent();\r
4861       break;\r
4862 \r
4863     case IDM_ReloadCMailMsg:\r
4864       Reset(TRUE, TRUE);\r
4865       ReloadCmailMsgEvent(FALSE);\r
4866       break;\r
4867 \r
4868     case IDM_Minimize:\r
4869       ShowWindow(hwnd, SW_MINIMIZE);\r
4870       break;\r
4871 \r
4872     case IDM_Exit:\r
4873       ExitEvent(0);\r
4874       break;\r
4875 \r
4876     case IDM_MachineWhite:\r
4877       MachineWhiteEvent();\r
4878       /*\r
4879        * refresh the tags dialog only if it's visible\r
4880        */\r
4881       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4882           char *tags;\r
4883           tags = PGNTags(&gameInfo);\r
4884           TagsPopUp(tags, CmailMsg());\r
4885           free(tags);\r
4886       }\r
4887       SAY("computer starts playing white");\r
4888       break;\r
4889 \r
4890     case IDM_MachineBlack:\r
4891       MachineBlackEvent();\r
4892       /*\r
4893        * refresh the tags dialog only if it's visible\r
4894        */\r
4895       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4896           char *tags;\r
4897           tags = PGNTags(&gameInfo);\r
4898           TagsPopUp(tags, CmailMsg());\r
4899           free(tags);\r
4900       }\r
4901       SAY("computer starts playing black");\r
4902       break;\r
4903 \r
4904     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4905       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4906       break;\r
4907 \r
4908     case IDM_TwoMachines:\r
4909       TwoMachinesEvent();\r
4910       /*\r
4911        * refresh the tags dialog only if it's visible\r
4912        */\r
4913       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4914           char *tags;\r
4915           tags = PGNTags(&gameInfo);\r
4916           TagsPopUp(tags, CmailMsg());\r
4917           free(tags);\r
4918       }\r
4919       SAY("computer starts playing both sides");\r
4920       break;\r
4921 \r
4922     case IDM_AnalysisMode:\r
4923       if (!first.analysisSupport) {\r
4924         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4925         DisplayError(buf, 0);\r
4926       } else {\r
4927         SAY("analyzing current position");\r
4928         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4929         if (appData.icsActive) {\r
4930                if (gameMode != IcsObserving) {\r
4931                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4932                        DisplayError(buf, 0);\r
4933                        /* secure check */\r
4934                        if (appData.icsEngineAnalyze) {\r
4935                                if (appData.debugMode) \r
4936                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4937                                ExitAnalyzeMode();\r
4938                                ModeHighlight();\r
4939                                break;\r
4940                        }\r
4941                        break;\r
4942                } else {\r
4943                        /* if enable, user want disable icsEngineAnalyze */\r
4944                        if (appData.icsEngineAnalyze) {\r
4945                                ExitAnalyzeMode();\r
4946                                ModeHighlight();\r
4947                                break;\r
4948                        }\r
4949                        appData.icsEngineAnalyze = TRUE;\r
4950                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4951                }\r
4952         } \r
4953         if (!appData.showThinking) ToggleShowThinking();\r
4954         AnalyzeModeEvent();\r
4955       }\r
4956       break;\r
4957 \r
4958     case IDM_AnalyzeFile:\r
4959       if (!first.analysisSupport) {\r
4960         char buf[MSG_SIZ];\r
4961           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4962         DisplayError(buf, 0);\r
4963       } else {\r
4964         if (!appData.showThinking) ToggleShowThinking();\r
4965         AnalyzeFileEvent();\r
4966 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4967         AnalysisPeriodicEvent(1);\r
4968       }\r
4969       break;\r
4970 \r
4971     case IDM_IcsClient:\r
4972       IcsClientEvent();\r
4973       break;\r
4974 \r
4975     case IDM_EditGame:\r
4976     case IDM_EditGame2:\r
4977       EditGameEvent();\r
4978       SAY("edit game");\r
4979       break;\r
4980 \r
4981     case IDM_EditPosition:\r
4982     case IDM_EditPosition2:\r
4983       EditPositionEvent();\r
4984       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4985       break;\r
4986 \r
4987     case IDM_Training:\r
4988       TrainingEvent();\r
4989       break;\r
4990 \r
4991     case IDM_ShowGameList:\r
4992       ShowGameListProc();\r
4993       break;\r
4994 \r
4995     case IDM_EditProgs1:\r
4996       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4997       break;\r
4998 \r
4999     case IDM_EditProgs2:\r
5000      LoadEnginePopUp(hwndMain);\r
5001 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
5002       break;\r
5003 \r
5004     case IDM_EditServers:\r
5005       EditTagsPopUp(icsNames, &icsNames);\r
5006       break;\r
5007 \r
5008     case IDM_EditTags:\r
5009     case IDM_Tags:\r
5010       EditTagsProc();\r
5011       break;\r
5012 \r
5013     case IDM_EditBook:\r
5014       EditBookEvent();\r
5015       break;\r
5016 \r
5017     case IDM_EditComment:\r
5018     case IDM_Comment:\r
5019       if (commentUp && editComment) {\r
5020         CommentPopDown();\r
5021       } else {\r
5022         EditCommentEvent();\r
5023       }\r
5024       break;\r
5025 \r
5026     case IDM_Pause:\r
5027       PauseEvent();\r
5028       break;\r
5029 \r
5030     case IDM_Accept:\r
5031       AcceptEvent();\r
5032       break;\r
5033 \r
5034     case IDM_Decline:\r
5035       DeclineEvent();\r
5036       break;\r
5037 \r
5038     case IDM_Rematch:\r
5039       RematchEvent();\r
5040       break;\r
5041 \r
5042     case IDM_CallFlag:\r
5043       CallFlagEvent();\r
5044       break;\r
5045 \r
5046     case IDM_Draw:\r
5047       DrawEvent();\r
5048       break;\r
5049 \r
5050     case IDM_Adjourn:\r
5051       AdjournEvent();\r
5052       break;\r
5053 \r
5054     case IDM_Abort:\r
5055       AbortEvent();\r
5056       break;\r
5057 \r
5058     case IDM_Resign:\r
5059       ResignEvent();\r
5060       break;\r
5061 \r
5062     case IDM_StopObserving:\r
5063       StopObservingEvent();\r
5064       break;\r
5065 \r
5066     case IDM_StopExamining:\r
5067       StopExaminingEvent();\r
5068       break;\r
5069 \r
5070     case IDM_Upload:\r
5071       UploadGameEvent();\r
5072       break;\r
5073 \r
5074     case IDM_TypeInMove:\r
5075       TypeInEvent('\000');\r
5076       break;\r
5077 \r
5078     case IDM_TypeInName:\r
5079       PopUpNameDialog('\000');\r
5080       break;\r
5081 \r
5082     case IDM_Backward:\r
5083       BackwardEvent();\r
5084       SetFocus(hwndMain);\r
5085       break;\r
5086 \r
5087     JAWS_MENU_ITEMS\r
5088 \r
5089     case IDM_Forward:\r
5090       ForwardEvent();\r
5091       SetFocus(hwndMain);\r
5092       break;\r
5093 \r
5094     case IDM_ToStart:\r
5095       ToStartEvent();\r
5096       SetFocus(hwndMain);\r
5097       break;\r
5098 \r
5099     case IDM_ToEnd:\r
5100       ToEndEvent();\r
5101       SetFocus(hwndMain);\r
5102       break;\r
5103 \r
5104     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5105     case OPT_GameListPrev:\r
5106       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5107       break;\r
5108 \r
5109     case IDM_Revert:\r
5110       RevertEvent(FALSE);\r
5111       break;\r
5112 \r
5113     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5114       RevertEvent(TRUE);\r
5115       break;\r
5116 \r
5117     case IDM_TruncateGame:\r
5118       TruncateGameEvent();\r
5119       break;\r
5120 \r
5121     case IDM_MoveNow:\r
5122       MoveNowEvent();\r
5123       break;\r
5124 \r
5125     case IDM_RetractMove:\r
5126       RetractMoveEvent();\r
5127       break;\r
5128 \r
5129     case IDM_FlipView:\r
5130       flipView = !flipView;\r
5131       DrawPosition(FALSE, NULL);\r
5132       break;\r
5133 \r
5134     case IDM_FlipClock:\r
5135       flipClock = !flipClock;\r
5136       DisplayBothClocks();\r
5137       DisplayLogos();\r
5138       break;\r
5139 \r
5140     case IDM_MuteSounds:\r
5141       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5142       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5143                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5144       break;\r
5145 \r
5146     case IDM_GeneralOptions:\r
5147       GeneralOptionsPopup(hwnd);\r
5148       DrawPosition(TRUE, NULL);\r
5149       break;\r
5150 \r
5151     case IDM_BoardOptions:\r
5152       BoardOptionsPopup(hwnd);\r
5153       break;\r
5154 \r
5155     case IDM_EnginePlayOptions:\r
5156       EnginePlayOptionsPopup(hwnd);\r
5157       break;\r
5158 \r
5159     case IDM_Engine1Options:\r
5160       EngineOptionsPopup(hwnd, &first);\r
5161       break;\r
5162 \r
5163     case IDM_Engine2Options:\r
5164       savedHwnd = hwnd;\r
5165       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5166       EngineOptionsPopup(hwnd, &second);\r
5167       break;\r
5168 \r
5169     case IDM_OptionsUCI:\r
5170       UciOptionsPopup(hwnd);\r
5171       break;\r
5172 \r
5173     case IDM_Tourney:\r
5174       TourneyPopup(hwnd);\r
5175       break;\r
5176 \r
5177     case IDM_IcsOptions:\r
5178       IcsOptionsPopup(hwnd);\r
5179       break;\r
5180 \r
5181     case IDM_Fonts:\r
5182       FontsOptionsPopup(hwnd);\r
5183       break;\r
5184 \r
5185     case IDM_Sounds:\r
5186       SoundOptionsPopup(hwnd);\r
5187       break;\r
5188 \r
5189     case IDM_CommPort:\r
5190       CommPortOptionsPopup(hwnd);\r
5191       break;\r
5192 \r
5193     case IDM_LoadOptions:\r
5194       LoadOptionsPopup(hwnd);\r
5195       break;\r
5196 \r
5197     case IDM_SaveOptions:\r
5198       SaveOptionsPopup(hwnd);\r
5199       break;\r
5200 \r
5201     case IDM_TimeControl:\r
5202       TimeControlOptionsPopup(hwnd);\r
5203       break;\r
5204 \r
5205     case IDM_SaveSettings:\r
5206       SaveSettings(settingsFileName);\r
5207       break;\r
5208 \r
5209     case IDM_SaveSettingsOnExit:\r
5210       saveSettingsOnExit = !saveSettingsOnExit;\r
5211       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5212                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5213                                          MF_CHECKED : MF_UNCHECKED));\r
5214       break;\r
5215 \r
5216     case IDM_Hint:\r
5217       HintEvent();\r
5218       break;\r
5219 \r
5220     case IDM_Book:\r
5221       BookEvent();\r
5222       break;\r
5223 \r
5224     case IDM_AboutGame:\r
5225       AboutGameEvent();\r
5226       break;\r
5227 \r
5228     case IDM_Debug:\r
5229       appData.debugMode = !appData.debugMode;\r
5230       if (appData.debugMode) {\r
5231         char dir[MSG_SIZ];\r
5232         GetCurrentDirectory(MSG_SIZ, dir);\r
5233         SetCurrentDirectory(installDir);\r
5234         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5235         SetCurrentDirectory(dir);\r
5236         setbuf(debugFP, NULL);\r
5237       } else {\r
5238         fclose(debugFP);\r
5239         debugFP = NULL;\r
5240       }\r
5241       break;\r
5242 \r
5243     case IDM_HELPCONTENTS:\r
5244       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5245           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5246           MessageBox (GetFocus(),\r
5247                     _("Unable to activate help"),\r
5248                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5249       }\r
5250       break;\r
5251 \r
5252     case IDM_HELPSEARCH:\r
5253         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5254             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5255         MessageBox (GetFocus(),\r
5256                     _("Unable to activate help"),\r
5257                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5258       }\r
5259       break;\r
5260 \r
5261     case IDM_HELPHELP:\r
5262       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5263         MessageBox (GetFocus(),\r
5264                     _("Unable to activate help"),\r
5265                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5266       }\r
5267       break;\r
5268 \r
5269     case IDM_ABOUT:\r
5270       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5271       DialogBox(hInst, \r
5272         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5273         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5274       FreeProcInstance(lpProc);\r
5275       break;\r
5276 \r
5277     case IDM_DirectCommand1:\r
5278       AskQuestionEvent(_("Direct Command"),\r
5279                        _("Send to chess program:"), "", "1");\r
5280       break;\r
5281     case IDM_DirectCommand2:\r
5282       AskQuestionEvent(_("Direct Command"),\r
5283                        _("Send to second chess program:"), "", "2");\r
5284       break;\r
5285 \r
5286     case EP_WhitePawn:\r
5287       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_WhiteKnight:\r
5292       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_WhiteBishop:\r
5297       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_WhiteRook:\r
5302       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_WhiteQueen:\r
5307       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_WhiteFerz:\r
5312       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_WhiteWazir:\r
5317       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_WhiteAlfil:\r
5322       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_WhiteCannon:\r
5327       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_WhiteCardinal:\r
5332       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_WhiteMarshall:\r
5337       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_WhiteKing:\r
5342       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_BlackPawn:\r
5347       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_BlackKnight:\r
5352       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_BlackBishop:\r
5357       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_BlackRook:\r
5362       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_BlackQueen:\r
5367       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_BlackFerz:\r
5372       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_BlackWazir:\r
5377       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_BlackAlfil:\r
5382       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_BlackCannon:\r
5387       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_BlackCardinal:\r
5392       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_BlackMarshall:\r
5397       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_BlackKing:\r
5402       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_EmptySquare:\r
5407       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_ClearBoard:\r
5412       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_White:\r
5417       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_Black:\r
5422       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_Promote:\r
5427       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_Demote:\r
5432       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case DP_Pawn:\r
5437       DropMenuEvent(WhitePawn, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case DP_Knight:\r
5442       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case DP_Bishop:\r
5447       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case DP_Rook:\r
5452       DropMenuEvent(WhiteRook, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case DP_Queen:\r
5457       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case IDM_English:\r
5462       barbaric = 0; appData.language = "";\r
5463       TranslateMenus(0);\r
5464       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5465       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5466       lastChecked = wmId;\r
5467       break;\r
5468 \r
5469     default:\r
5470       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5471           RecentEngineEvent(wmId - 3000);\r
5472       else\r
5473       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5474           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5475           TranslateMenus(0);\r
5476           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5477           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5478           lastChecked = wmId;\r
5479           break;\r
5480       }\r
5481       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5482     }\r
5483     break;\r
5484 \r
5485   case WM_TIMER:\r
5486     switch (wParam) {\r
5487     case CLOCK_TIMER_ID:\r
5488       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5489       clockTimerEvent = 0;\r
5490       DecrementClocks(); /* call into back end */\r
5491       break;\r
5492     case LOAD_GAME_TIMER_ID:\r
5493       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5494       loadGameTimerEvent = 0;\r
5495       AutoPlayGameLoop(); /* call into back end */\r
5496       break;\r
5497     case ANALYSIS_TIMER_ID:\r
5498       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5499                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5500         AnalysisPeriodicEvent(0);\r
5501       } else {\r
5502         KillTimer(hwnd, analysisTimerEvent);\r
5503         analysisTimerEvent = 0;\r
5504       }\r
5505       break;\r
5506     case DELAYED_TIMER_ID:\r
5507       KillTimer(hwnd, delayedTimerEvent);\r
5508       delayedTimerEvent = 0;\r
5509       delayedTimerCallback();\r
5510       break;\r
5511     }\r
5512     break;\r
5513 \r
5514   case WM_USER_Input:\r
5515     InputEvent(hwnd, message, wParam, lParam);\r
5516     break;\r
5517 \r
5518   /* [AS] Also move "attached" child windows */\r
5519   case WM_WINDOWPOSCHANGING:\r
5520 \r
5521     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5522         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5523 \r
5524         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5525             /* Window is moving */\r
5526             RECT rcMain;\r
5527 \r
5528 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5529             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5530             rcMain.right  = wpMain.x + wpMain.width;\r
5531             rcMain.top    = wpMain.y;\r
5532             rcMain.bottom = wpMain.y + wpMain.height;\r
5533             \r
5534             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5535             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5536             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5537             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5538             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5539             wpMain.x = lpwp->x;\r
5540             wpMain.y = lpwp->y;\r
5541         }\r
5542     }\r
5543     break;\r
5544 \r
5545   /* [AS] Snapping */\r
5546   case WM_ENTERSIZEMOVE:\r
5547     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5548     if (hwnd == hwndMain) {\r
5549       doingSizing = TRUE;\r
5550       lastSizing = 0;\r
5551     }\r
5552     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5553     break;\r
5554 \r
5555   case WM_SIZING:\r
5556     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5557     if (hwnd == hwndMain) {\r
5558       lastSizing = wParam;\r
5559     }\r
5560     break;\r
5561 \r
5562   case WM_MOVING:\r
5563     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5564       return OnMoving( &sd, hwnd, wParam, lParam );\r
5565 \r
5566   case WM_EXITSIZEMOVE:\r
5567     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5568     if (hwnd == hwndMain) {\r
5569       RECT client;\r
5570       doingSizing = FALSE;\r
5571       InvalidateRect(hwnd, &boardRect, FALSE);\r
5572       GetClientRect(hwnd, &client);\r
5573       ResizeBoard(client.right, client.bottom, lastSizing);\r
5574       lastSizing = 0;\r
5575       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5576     }\r
5577     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5578     break;\r
5579 \r
5580   case WM_DESTROY: /* message: window being destroyed */\r
5581     PostQuitMessage(0);\r
5582     break;\r
5583 \r
5584   case WM_CLOSE:\r
5585     if (hwnd == hwndMain) {\r
5586       ExitEvent(0);\r
5587     }\r
5588     break;\r
5589 \r
5590   default:      /* Passes it on if unprocessed */\r
5591     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5592   }\r
5593   return 0;\r
5594 }\r
5595 \r
5596 /*---------------------------------------------------------------------------*\\r
5597  *\r
5598  * Misc utility routines\r
5599  *\r
5600 \*---------------------------------------------------------------------------*/\r
5601 \r
5602 /*\r
5603  * Decent random number generator, at least not as bad as Windows\r
5604  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5605  */\r
5606 unsigned int randstate;\r
5607 \r
5608 int\r
5609 myrandom(void)\r
5610 {\r
5611   randstate = randstate * 1664525 + 1013904223;\r
5612   return (int) randstate & 0x7fffffff;\r
5613 }\r
5614 \r
5615 void\r
5616 mysrandom(unsigned int seed)\r
5617 {\r
5618   randstate = seed;\r
5619 }\r
5620 \r
5621 \r
5622 /* \r
5623  * returns TRUE if user selects a different color, FALSE otherwise \r
5624  */\r
5625 \r
5626 BOOL\r
5627 ChangeColor(HWND hwnd, COLORREF *which)\r
5628 {\r
5629   static BOOL firstTime = TRUE;\r
5630   static DWORD customColors[16];\r
5631   CHOOSECOLOR cc;\r
5632   COLORREF newcolor;\r
5633   int i;\r
5634   ColorClass ccl;\r
5635 \r
5636   if (firstTime) {\r
5637     /* Make initial colors in use available as custom colors */\r
5638     /* Should we put the compiled-in defaults here instead? */\r
5639     i = 0;\r
5640     customColors[i++] = lightSquareColor & 0xffffff;\r
5641     customColors[i++] = darkSquareColor & 0xffffff;\r
5642     customColors[i++] = whitePieceColor & 0xffffff;\r
5643     customColors[i++] = blackPieceColor & 0xffffff;\r
5644     customColors[i++] = highlightSquareColor & 0xffffff;\r
5645     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5646 \r
5647     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5648       customColors[i++] = textAttribs[ccl].color;\r
5649     }\r
5650     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5651     firstTime = FALSE;\r
5652   }\r
5653 \r
5654   cc.lStructSize = sizeof(cc);\r
5655   cc.hwndOwner = hwnd;\r
5656   cc.hInstance = NULL;\r
5657   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5658   cc.lpCustColors = (LPDWORD) customColors;\r
5659   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5660 \r
5661   if (!ChooseColor(&cc)) return FALSE;\r
5662 \r
5663   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5664   if (newcolor == *which) return FALSE;\r
5665   *which = newcolor;\r
5666   return TRUE;\r
5667 \r
5668   /*\r
5669   InitDrawingColors();\r
5670   InvalidateRect(hwnd, &boardRect, FALSE);\r
5671   */\r
5672 }\r
5673 \r
5674 BOOLEAN\r
5675 MyLoadSound(MySound *ms)\r
5676 {\r
5677   BOOL ok = FALSE;\r
5678   struct stat st;\r
5679   FILE *f;\r
5680 \r
5681   if (ms->data && ms->flag) free(ms->data);\r
5682   ms->data = NULL;\r
5683 \r
5684   switch (ms->name[0]) {\r
5685   case NULLCHAR:\r
5686     /* Silence */\r
5687     ok = TRUE;\r
5688     break;\r
5689   case '$':\r
5690     /* System sound from Control Panel.  Don't preload here. */\r
5691     ok = TRUE;\r
5692     break;\r
5693   case '!':\r
5694     if (ms->name[1] == NULLCHAR) {\r
5695       /* "!" alone = silence */\r
5696       ok = TRUE;\r
5697     } else {\r
5698       /* Builtin wave resource.  Error if not found. */\r
5699       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5700       if (h == NULL) break;\r
5701       ms->data = (void *)LoadResource(hInst, h);\r
5702       ms->flag = 0; // not maloced, so cannot be freed!\r
5703       if (h == NULL) break;\r
5704       ok = TRUE;\r
5705     }\r
5706     break;\r
5707   default:\r
5708     /* .wav file.  Error if not found. */\r
5709     f = fopen(ms->name, "rb");\r
5710     if (f == NULL) break;\r
5711     if (fstat(fileno(f), &st) < 0) break;\r
5712     ms->data = malloc(st.st_size);\r
5713     ms->flag = 1;\r
5714     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5715     fclose(f);\r
5716     ok = TRUE;\r
5717     break;\r
5718   }\r
5719   if (!ok) {\r
5720     char buf[MSG_SIZ];\r
5721       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5722     DisplayError(buf, GetLastError());\r
5723   }\r
5724   return ok;\r
5725 }\r
5726 \r
5727 BOOLEAN\r
5728 MyPlaySound(MySound *ms)\r
5729 {\r
5730   BOOLEAN ok = FALSE;\r
5731 \r
5732   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5733   switch (ms->name[0]) {\r
5734   case NULLCHAR:\r
5735         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5736     /* Silence */\r
5737     ok = TRUE;\r
5738     break;\r
5739   case '$':\r
5740     /* System sound from Control Panel (deprecated feature).\r
5741        "$" alone or an unset sound name gets default beep (still in use). */\r
5742     if (ms->name[1]) {\r
5743       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5744     }\r
5745     if (!ok) ok = MessageBeep(MB_OK);\r
5746     break; \r
5747   case '!':\r
5748     /* Builtin wave resource, or "!" alone for silence */\r
5749     if (ms->name[1]) {\r
5750       if (ms->data == NULL) return FALSE;\r
5751       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5752     } else {\r
5753       ok = TRUE;\r
5754     }\r
5755     break;\r
5756   default:\r
5757     /* .wav file.  Error if not found. */\r
5758     if (ms->data == NULL) return FALSE;\r
5759     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5760     break;\r
5761   }\r
5762   /* Don't print an error: this can happen innocently if the sound driver\r
5763      is busy; for instance, if another instance of WinBoard is playing\r
5764      a sound at about the same time. */\r
5765   return ok;\r
5766 }\r
5767 \r
5768 \r
5769 LRESULT CALLBACK\r
5770 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5771 {\r
5772   BOOL ok;\r
5773   OPENFILENAME *ofn;\r
5774   static UINT *number; /* gross that this is static */\r
5775 \r
5776   switch (message) {\r
5777   case WM_INITDIALOG: /* message: initialize dialog box */\r
5778     /* Center the dialog over the application window */\r
5779     ofn = (OPENFILENAME *) lParam;\r
5780     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5781       number = (UINT *) ofn->lCustData;\r
5782       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5783     } else {\r
5784       number = NULL;\r
5785     }\r
5786     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5787     Translate(hDlg, 1536);\r
5788     return FALSE;  /* Allow for further processing */\r
5789 \r
5790   case WM_COMMAND:\r
5791     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5792       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5793     }\r
5794     return FALSE;  /* Allow for further processing */\r
5795   }\r
5796   return FALSE;\r
5797 }\r
5798 \r
5799 UINT APIENTRY\r
5800 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5801 {\r
5802   static UINT *number;\r
5803   OPENFILENAME *ofname;\r
5804   OFNOTIFY *ofnot;\r
5805   switch (uiMsg) {\r
5806   case WM_INITDIALOG:\r
5807     Translate(hdlg, DLG_IndexNumber);\r
5808     ofname = (OPENFILENAME *)lParam;\r
5809     number = (UINT *)(ofname->lCustData);\r
5810     break;\r
5811   case WM_NOTIFY:\r
5812     ofnot = (OFNOTIFY *)lParam;\r
5813     if (ofnot->hdr.code == CDN_FILEOK) {\r
5814       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5815     }\r
5816     break;\r
5817   }\r
5818   return 0;\r
5819 }\r
5820 \r
5821 \r
5822 FILE *\r
5823 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5824                char *nameFilt, char *dlgTitle, UINT *number,\r
5825                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5826 {\r
5827   OPENFILENAME openFileName;\r
5828   char buf1[MSG_SIZ];\r
5829   FILE *f;\r
5830 \r
5831   if (fileName == NULL) fileName = buf1;\r
5832   if (defName == NULL) {\r
5833     safeStrCpy(fileName, "*.", 3 );\r
5834     strcat(fileName, defExt);\r
5835   } else {\r
5836     safeStrCpy(fileName, defName, MSG_SIZ );\r
5837   }\r
5838     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5839   if (number) *number = 0;\r
5840 \r
5841   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5842   openFileName.hwndOwner         = hwnd;\r
5843   openFileName.hInstance         = (HANDLE) hInst;\r
5844   openFileName.lpstrFilter       = nameFilt;\r
5845   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5846   openFileName.nMaxCustFilter    = 0L;\r
5847   openFileName.nFilterIndex      = 1L;\r
5848   openFileName.lpstrFile         = fileName;\r
5849   openFileName.nMaxFile          = MSG_SIZ;\r
5850   openFileName.lpstrFileTitle    = fileTitle;\r
5851   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5852   openFileName.lpstrInitialDir   = NULL;\r
5853   openFileName.lpstrTitle        = dlgTitle;\r
5854   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5855     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5856     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5857     | (oldDialog ? 0 : OFN_EXPLORER);\r
5858   openFileName.nFileOffset       = 0;\r
5859   openFileName.nFileExtension    = 0;\r
5860   openFileName.lpstrDefExt       = defExt;\r
5861   openFileName.lCustData         = (LONG) number;\r
5862   openFileName.lpfnHook          = oldDialog ?\r
5863     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5864   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5865 \r
5866   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5867                         GetOpenFileName(&openFileName)) {\r
5868     /* open the file */\r
5869     f = fopen(openFileName.lpstrFile, write);\r
5870     if (f == NULL) {\r
5871       MessageBox(hwnd, _("File open failed"), NULL,\r
5872                  MB_OK|MB_ICONEXCLAMATION);\r
5873       return NULL;\r
5874     }\r
5875   } else {\r
5876     int err = CommDlgExtendedError();\r
5877     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5878     return FALSE;\r
5879   }\r
5880   return f;\r
5881 }\r
5882 \r
5883 \r
5884 \r
5885 VOID APIENTRY\r
5886 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5887 {\r
5888   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5889 \r
5890   /*\r
5891    * Get the first pop-up menu in the menu template. This is the\r
5892    * menu that TrackPopupMenu displays.\r
5893    */\r
5894   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5895   TranslateOneMenu(10, hmenuTrackPopup);\r
5896 \r
5897   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5898 \r
5899   /*\r
5900    * TrackPopup uses screen coordinates, so convert the\r
5901    * coordinates of the mouse click to screen coordinates.\r
5902    */\r
5903   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5904 \r
5905   /* Draw and track the floating pop-up menu. */\r
5906   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5907                  pt.x, pt.y, 0, hwnd, NULL);\r
5908 \r
5909   /* Destroy the menu.*/\r
5910   DestroyMenu(hmenu);\r
5911 }\r
5912    \r
5913 typedef struct {\r
5914   HWND hDlg, hText;\r
5915   int sizeX, sizeY, newSizeX, newSizeY;\r
5916   HDWP hdwp;\r
5917 } ResizeEditPlusButtonsClosure;\r
5918 \r
5919 BOOL CALLBACK\r
5920 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5921 {\r
5922   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5923   RECT rect;\r
5924   POINT pt;\r
5925 \r
5926   if (hChild == cl->hText) return TRUE;\r
5927   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5928   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5929   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5930   ScreenToClient(cl->hDlg, &pt);\r
5931   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5932     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5933   return TRUE;\r
5934 }\r
5935 \r
5936 /* Resize a dialog that has a (rich) edit field filling most of\r
5937    the top, with a row of buttons below */\r
5938 VOID\r
5939 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5940 {\r
5941   RECT rectText;\r
5942   int newTextHeight, newTextWidth;\r
5943   ResizeEditPlusButtonsClosure cl;\r
5944   \r
5945   /*if (IsIconic(hDlg)) return;*/\r
5946   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5947   \r
5948   cl.hdwp = BeginDeferWindowPos(8);\r
5949 \r
5950   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5951   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5952   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5953   if (newTextHeight < 0) {\r
5954     newSizeY += -newTextHeight;\r
5955     newTextHeight = 0;\r
5956   }\r
5957   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5958     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5959 \r
5960   cl.hDlg = hDlg;\r
5961   cl.hText = hText;\r
5962   cl.sizeX = sizeX;\r
5963   cl.sizeY = sizeY;\r
5964   cl.newSizeX = newSizeX;\r
5965   cl.newSizeY = newSizeY;\r
5966   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5967 \r
5968   EndDeferWindowPos(cl.hdwp);\r
5969 }\r
5970 \r
5971 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5972 {\r
5973     RECT    rChild, rParent;\r
5974     int     wChild, hChild, wParent, hParent;\r
5975     int     wScreen, hScreen, xNew, yNew;\r
5976     HDC     hdc;\r
5977 \r
5978     /* Get the Height and Width of the child window */\r
5979     GetWindowRect (hwndChild, &rChild);\r
5980     wChild = rChild.right - rChild.left;\r
5981     hChild = rChild.bottom - rChild.top;\r
5982 \r
5983     /* Get the Height and Width of the parent window */\r
5984     GetWindowRect (hwndParent, &rParent);\r
5985     wParent = rParent.right - rParent.left;\r
5986     hParent = rParent.bottom - rParent.top;\r
5987 \r
5988     /* Get the display limits */\r
5989     hdc = GetDC (hwndChild);\r
5990     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5991     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5992     ReleaseDC(hwndChild, hdc);\r
5993 \r
5994     /* Calculate new X position, then adjust for screen */\r
5995     xNew = rParent.left + ((wParent - wChild) /2);\r
5996     if (xNew < 0) {\r
5997         xNew = 0;\r
5998     } else if ((xNew+wChild) > wScreen) {\r
5999         xNew = wScreen - wChild;\r
6000     }\r
6001 \r
6002     /* Calculate new Y position, then adjust for screen */\r
6003     if( mode == 0 ) {\r
6004         yNew = rParent.top  + ((hParent - hChild) /2);\r
6005     }\r
6006     else {\r
6007         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6008     }\r
6009 \r
6010     if (yNew < 0) {\r
6011         yNew = 0;\r
6012     } else if ((yNew+hChild) > hScreen) {\r
6013         yNew = hScreen - hChild;\r
6014     }\r
6015 \r
6016     /* Set it, and return */\r
6017     return SetWindowPos (hwndChild, NULL,\r
6018                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6019 }\r
6020 \r
6021 /* Center one window over another */\r
6022 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6023 {\r
6024     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6025 }\r
6026 \r
6027 /*---------------------------------------------------------------------------*\\r
6028  *\r
6029  * Startup Dialog functions\r
6030  *\r
6031 \*---------------------------------------------------------------------------*/\r
6032 void\r
6033 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6034 {\r
6035   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6036 \r
6037   while (*cd != NULL) {\r
6038     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6039     cd++;\r
6040   }\r
6041 }\r
6042 \r
6043 void\r
6044 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6045 {\r
6046   char buf1[MAX_ARG_LEN];\r
6047   int len;\r
6048 \r
6049   if (str[0] == '@') {\r
6050     FILE* f = fopen(str + 1, "r");\r
6051     if (f == NULL) {\r
6052       DisplayFatalError(str + 1, errno, 2);\r
6053       return;\r
6054     }\r
6055     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6056     fclose(f);\r
6057     buf1[len] = NULLCHAR;\r
6058     str = buf1;\r
6059   }\r
6060 \r
6061   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6062 \r
6063   for (;;) {\r
6064     char buf[MSG_SIZ];\r
6065     char *end = strchr(str, '\n');\r
6066     if (end == NULL) return;\r
6067     memcpy(buf, str, end - str);\r
6068     buf[end - str] = NULLCHAR;\r
6069     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6070     str = end + 1;\r
6071   }\r
6072 }\r
6073 \r
6074 void\r
6075 SetStartupDialogEnables(HWND hDlg)\r
6076 {\r
6077   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6078     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6079     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6080   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6081     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6082   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6083     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6084   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6085     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6086   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6087     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6088     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6089     IsDlgButtonChecked(hDlg, OPT_View));\r
6090 }\r
6091 \r
6092 char *\r
6093 QuoteForFilename(char *filename)\r
6094 {\r
6095   int dquote, space;\r
6096   dquote = strchr(filename, '"') != NULL;\r
6097   space = strchr(filename, ' ') != NULL;\r
6098   if (dquote || space) {\r
6099     if (dquote) {\r
6100       return "'";\r
6101     } else {\r
6102       return "\"";\r
6103     }\r
6104   } else {\r
6105     return "";\r
6106   }\r
6107 }\r
6108 \r
6109 VOID\r
6110 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6111 {\r
6112   char buf[MSG_SIZ];\r
6113   char *q;\r
6114 \r
6115   InitComboStringsFromOption(hwndCombo, nthnames);\r
6116   q = QuoteForFilename(nthcp);\r
6117     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6118   if (*nthdir != NULLCHAR) {\r
6119     q = QuoteForFilename(nthdir);\r
6120       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6121   }\r
6122   if (*nthcp == NULLCHAR) {\r
6123     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6124   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6125     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6126     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6127   }\r
6128 }\r
6129 \r
6130 LRESULT CALLBACK\r
6131 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6132 {\r
6133   char buf[MSG_SIZ];\r
6134   HANDLE hwndCombo;\r
6135   char *p;\r
6136 \r
6137   switch (message) {\r
6138   case WM_INITDIALOG:\r
6139     /* Center the dialog */\r
6140     CenterWindow (hDlg, GetDesktopWindow());\r
6141     Translate(hDlg, DLG_Startup);\r
6142     /* Initialize the dialog items */\r
6143     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6144                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6145                   firstChessProgramNames);\r
6146     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6147                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6148                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6149     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6150     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6151       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6152     if (*appData.icsHelper != NULLCHAR) {\r
6153       char *q = QuoteForFilename(appData.icsHelper);\r
6154       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6155     }\r
6156     if (*appData.icsHost == NULLCHAR) {\r
6157       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6158       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6159     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6160       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6161       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6162     }\r
6163 \r
6164     if (appData.icsActive) {\r
6165       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6166     }\r
6167     else if (appData.noChessProgram) {\r
6168       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6169     }\r
6170     else {\r
6171       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6172     }\r
6173 \r
6174     SetStartupDialogEnables(hDlg);\r
6175     return TRUE;\r
6176 \r
6177   case WM_COMMAND:\r
6178     switch (LOWORD(wParam)) {\r
6179     case IDOK:\r
6180       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6181         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6182         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6183         p = buf;\r
6184         ParseArgs(StringGet, &p);\r
6185         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6186         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6187         p = buf;\r
6188         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6189         ParseArgs(StringGet, &p);\r
6190         SwapEngines(singleList); // ... and then make it 'second'\r
6191         appData.noChessProgram = FALSE;\r
6192         appData.icsActive = FALSE;\r
6193       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6194         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6195         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6196         p = buf;\r
6197         ParseArgs(StringGet, &p);\r
6198         if (appData.zippyPlay) {\r
6199           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6200           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6201           p = buf;\r
6202           ParseArgs(StringGet, &p);\r
6203         }\r
6204       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6205         appData.noChessProgram = TRUE;\r
6206         appData.icsActive = FALSE;\r
6207       } else {\r
6208         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6209                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6210         return TRUE;\r
6211       }\r
6212       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6213         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6214         p = buf;\r
6215         ParseArgs(StringGet, &p);\r
6216       }\r
6217       EndDialog(hDlg, TRUE);\r
6218       return TRUE;\r
6219 \r
6220     case IDCANCEL:\r
6221       ExitEvent(0);\r
6222       return TRUE;\r
6223 \r
6224     case IDM_HELPCONTENTS:\r
6225       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6226         MessageBox (GetFocus(),\r
6227                     _("Unable to activate help"),\r
6228                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6229       }\r
6230       break;\r
6231 \r
6232     default:\r
6233       SetStartupDialogEnables(hDlg);\r
6234       break;\r
6235     }\r
6236     break;\r
6237   }\r
6238   return FALSE;\r
6239 }\r
6240 \r
6241 /*---------------------------------------------------------------------------*\\r
6242  *\r
6243  * About box dialog functions\r
6244  *\r
6245 \*---------------------------------------------------------------------------*/\r
6246 \r
6247 /* Process messages for "About" dialog box */\r
6248 LRESULT CALLBACK\r
6249 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6250 {\r
6251   switch (message) {\r
6252   case WM_INITDIALOG: /* message: initialize dialog box */\r
6253     /* Center the dialog over the application window */\r
6254     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6255     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6256     Translate(hDlg, ABOUTBOX);\r
6257     JAWS_COPYRIGHT\r
6258     return (TRUE);\r
6259 \r
6260   case WM_COMMAND: /* message: received a command */\r
6261     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6262         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6263       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6264       return (TRUE);\r
6265     }\r
6266     break;\r
6267   }\r
6268   return (FALSE);\r
6269 }\r
6270 \r
6271 /*---------------------------------------------------------------------------*\\r
6272  *\r
6273  * Comment Dialog functions\r
6274  *\r
6275 \*---------------------------------------------------------------------------*/\r
6276 \r
6277 LRESULT CALLBACK\r
6278 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6279 {\r
6280   static HANDLE hwndText = NULL;\r
6281   int len, newSizeX, newSizeY, flags;\r
6282   static int sizeX, sizeY;\r
6283   char *str;\r
6284   RECT rect;\r
6285   MINMAXINFO *mmi;\r
6286 \r
6287   switch (message) {\r
6288   case WM_INITDIALOG: /* message: initialize dialog box */\r
6289     /* Initialize the dialog items */\r
6290     Translate(hDlg, DLG_EditComment);\r
6291     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6292     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6293     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6294     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6295     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6296     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6297     SetWindowText(hDlg, commentTitle);\r
6298     if (editComment) {\r
6299       SetFocus(hwndText);\r
6300     } else {\r
6301       SetFocus(GetDlgItem(hDlg, IDOK));\r
6302     }\r
6303     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6304                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6305                 MAKELPARAM(FALSE, 0));\r
6306     /* Size and position the dialog */\r
6307     if (!commentDialog) {\r
6308       commentDialog = hDlg;\r
6309       flags = SWP_NOZORDER;\r
6310       GetClientRect(hDlg, &rect);\r
6311       sizeX = rect.right;\r
6312       sizeY = rect.bottom;\r
6313       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6314           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6315         WINDOWPLACEMENT wp;\r
6316         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6317         wp.length = sizeof(WINDOWPLACEMENT);\r
6318         wp.flags = 0;\r
6319         wp.showCmd = SW_SHOW;\r
6320         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6321         wp.rcNormalPosition.left = wpComment.x;\r
6322         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6323         wp.rcNormalPosition.top = wpComment.y;\r
6324         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6325         SetWindowPlacement(hDlg, &wp);\r
6326 \r
6327         GetClientRect(hDlg, &rect);\r
6328         newSizeX = rect.right;\r
6329         newSizeY = rect.bottom;\r
6330         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6331                               newSizeX, newSizeY);\r
6332         sizeX = newSizeX;\r
6333         sizeY = newSizeY;\r
6334       }\r
6335     }\r
6336     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6337     return FALSE;\r
6338 \r
6339   case WM_COMMAND: /* message: received a command */\r
6340     switch (LOWORD(wParam)) {\r
6341     case IDOK:\r
6342       if (editComment) {\r
6343         char *p, *q;\r
6344         /* Read changed options from the dialog box */\r
6345         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6346         len = GetWindowTextLength(hwndText);\r
6347         str = (char *) malloc(len + 1);\r
6348         GetWindowText(hwndText, str, len + 1);\r
6349         p = q = str;\r
6350         while (*q) {\r
6351           if (*q == '\r')\r
6352             q++;\r
6353           else\r
6354             *p++ = *q++;\r
6355         }\r
6356         *p = NULLCHAR;\r
6357         ReplaceComment(commentIndex, str);\r
6358         free(str);\r
6359       }\r
6360       CommentPopDown();\r
6361       return TRUE;\r
6362 \r
6363     case IDCANCEL:\r
6364     case OPT_CancelComment:\r
6365       CommentPopDown();\r
6366       return TRUE;\r
6367 \r
6368     case OPT_ClearComment:\r
6369       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6370       break;\r
6371 \r
6372     case OPT_EditComment:\r
6373       EditCommentEvent();\r
6374       return TRUE;\r
6375 \r
6376     default:\r
6377       break;\r
6378     }\r
6379     break;\r
6380 \r
6381   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6382         if( wParam == OPT_CommentText ) {\r
6383             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6384 \r
6385             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6386                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6387                 POINTL pt;\r
6388                 LRESULT index;\r
6389 \r
6390                 pt.x = LOWORD( lpMF->lParam );\r
6391                 pt.y = HIWORD( lpMF->lParam );\r
6392 \r
6393                 if(lpMF->msg == WM_CHAR) {\r
6394                         CHARRANGE sel;\r
6395                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6396                         index = sel.cpMin;\r
6397                 } else\r
6398                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6399 \r
6400                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6401                 len = GetWindowTextLength(hwndText);\r
6402                 str = (char *) malloc(len + 1);\r
6403                 GetWindowText(hwndText, str, len + 1);\r
6404                 ReplaceComment(commentIndex, str);\r
6405                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6406                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6407                 free(str);\r
6408 \r
6409                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6410                 lpMF->msg = WM_USER;\r
6411 \r
6412                 return TRUE;\r
6413             }\r
6414         }\r
6415         break;\r
6416 \r
6417   case WM_SIZE:\r
6418     newSizeX = LOWORD(lParam);\r
6419     newSizeY = HIWORD(lParam);\r
6420     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6421     sizeX = newSizeX;\r
6422     sizeY = newSizeY;\r
6423     break;\r
6424 \r
6425   case WM_GETMINMAXINFO:\r
6426     /* Prevent resizing window too small */\r
6427     mmi = (MINMAXINFO *) lParam;\r
6428     mmi->ptMinTrackSize.x = 100;\r
6429     mmi->ptMinTrackSize.y = 100;\r
6430     break;\r
6431   }\r
6432   return FALSE;\r
6433 }\r
6434 \r
6435 VOID\r
6436 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6437 {\r
6438   FARPROC lpProc;\r
6439   char *p, *q;\r
6440 \r
6441   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6442 \r
6443   if (str == NULL) str = "";\r
6444   p = (char *) malloc(2 * strlen(str) + 2);\r
6445   q = p;\r
6446   while (*str) {\r
6447     if (*str == '\n') *q++ = '\r';\r
6448     *q++ = *str++;\r
6449   }\r
6450   *q = NULLCHAR;\r
6451   if (commentText != NULL) free(commentText);\r
6452 \r
6453   commentIndex = index;\r
6454   commentTitle = title;\r
6455   commentText = p;\r
6456   editComment = edit;\r
6457 \r
6458   if (commentDialog) {\r
6459     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6460     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6461   } else {\r
6462     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6463     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6464                  hwndMain, (DLGPROC)lpProc);\r
6465     FreeProcInstance(lpProc);\r
6466   }\r
6467   commentUp = TRUE;\r
6468 }\r
6469 \r
6470 \r
6471 /*---------------------------------------------------------------------------*\\r
6472  *\r
6473  * Type-in move dialog functions\r
6474  * \r
6475 \*---------------------------------------------------------------------------*/\r
6476 \r
6477 LRESULT CALLBACK\r
6478 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6479 {\r
6480   char move[MSG_SIZ];\r
6481   HWND hInput;\r
6482 \r
6483   switch (message) {\r
6484   case WM_INITDIALOG:\r
6485     move[0] = (char) lParam;\r
6486     move[1] = NULLCHAR;\r
6487     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6488     Translate(hDlg, DLG_TypeInMove);\r
6489     hInput = GetDlgItem(hDlg, OPT_Move);\r
6490     SetWindowText(hInput, move);\r
6491     SetFocus(hInput);\r
6492     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6493     return FALSE;\r
6494 \r
6495   case WM_COMMAND:\r
6496     switch (LOWORD(wParam)) {\r
6497     case IDOK:\r
6498 \r
6499       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6500       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6501       TypeInDoneEvent(move);\r
6502       EndDialog(hDlg, TRUE);\r
6503       return TRUE;\r
6504     case IDCANCEL:\r
6505       EndDialog(hDlg, FALSE);\r
6506       return TRUE;\r
6507     default:\r
6508       break;\r
6509     }\r
6510     break;\r
6511   }\r
6512   return FALSE;\r
6513 }\r
6514 \r
6515 VOID\r
6516 PopUpMoveDialog(char firstchar)\r
6517 {\r
6518     FARPROC lpProc;\r
6519 \r
6520       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6521       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6522         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6523       FreeProcInstance(lpProc);\r
6524 }\r
6525 \r
6526 /*---------------------------------------------------------------------------*\\r
6527  *\r
6528  * Type-in name dialog functions\r
6529  * \r
6530 \*---------------------------------------------------------------------------*/\r
6531 \r
6532 LRESULT CALLBACK\r
6533 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6534 {\r
6535   char move[MSG_SIZ];\r
6536   HWND hInput;\r
6537 \r
6538   switch (message) {\r
6539   case WM_INITDIALOG:\r
6540     move[0] = (char) lParam;\r
6541     move[1] = NULLCHAR;\r
6542     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6543     Translate(hDlg, DLG_TypeInName);\r
6544     hInput = GetDlgItem(hDlg, OPT_Name);\r
6545     SetWindowText(hInput, move);\r
6546     SetFocus(hInput);\r
6547     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6548     return FALSE;\r
6549 \r
6550   case WM_COMMAND:\r
6551     switch (LOWORD(wParam)) {\r
6552     case IDOK:\r
6553       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6554       appData.userName = strdup(move);\r
6555       SetUserLogo();\r
6556       SetGameInfo();\r
6557       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6558         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6559         DisplayTitle(move);\r
6560       }\r
6561 \r
6562 \r
6563       EndDialog(hDlg, TRUE);\r
6564       return TRUE;\r
6565     case IDCANCEL:\r
6566       EndDialog(hDlg, FALSE);\r
6567       return TRUE;\r
6568     default:\r
6569       break;\r
6570     }\r
6571     break;\r
6572   }\r
6573   return FALSE;\r
6574 }\r
6575 \r
6576 VOID\r
6577 PopUpNameDialog(char firstchar)\r
6578 {\r
6579     FARPROC lpProc;\r
6580     \r
6581       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6582       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6583         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6584       FreeProcInstance(lpProc);\r
6585 }\r
6586 \r
6587 /*---------------------------------------------------------------------------*\\r
6588  *\r
6589  *  Error dialogs\r
6590  * \r
6591 \*---------------------------------------------------------------------------*/\r
6592 \r
6593 /* Nonmodal error box */\r
6594 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6595                              WPARAM wParam, LPARAM lParam);\r
6596 \r
6597 VOID\r
6598 ErrorPopUp(char *title, char *content)\r
6599 {\r
6600   FARPROC lpProc;\r
6601   char *p, *q;\r
6602   BOOLEAN modal = hwndMain == NULL;\r
6603 \r
6604   p = content;\r
6605   q = errorMessage;\r
6606   while (*p) {\r
6607     if (*p == '\n') {\r
6608       if (modal) {\r
6609         *q++ = ' ';\r
6610         p++;\r
6611       } else {\r
6612         *q++ = '\r';\r
6613         *q++ = *p++;\r
6614       }\r
6615     } else {\r
6616       *q++ = *p++;\r
6617     }\r
6618   }\r
6619   *q = NULLCHAR;\r
6620   strncpy(errorTitle, title, sizeof(errorTitle));\r
6621   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6622   \r
6623   if (modal) {\r
6624     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6625   } else {\r
6626     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6627     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6628                  hwndMain, (DLGPROC)lpProc);\r
6629     FreeProcInstance(lpProc);\r
6630   }\r
6631 }\r
6632 \r
6633 VOID\r
6634 ErrorPopDown()\r
6635 {\r
6636   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6637   if (errorDialog == NULL) return;\r
6638   DestroyWindow(errorDialog);\r
6639   errorDialog = NULL;\r
6640   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6641 }\r
6642 \r
6643 LRESULT CALLBACK\r
6644 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6645 {\r
6646   HANDLE hwndText;\r
6647   RECT rChild;\r
6648 \r
6649   switch (message) {\r
6650   case WM_INITDIALOG:\r
6651     GetWindowRect(hDlg, &rChild);\r
6652 \r
6653     /*\r
6654     SetWindowPos(hDlg, NULL, rChild.left,\r
6655       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6656       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6657     */\r
6658 \r
6659     /* \r
6660         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6661         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6662         and it doesn't work when you resize the dialog.\r
6663         For now, just give it a default position.\r
6664     */\r
6665     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6666     Translate(hDlg, DLG_Error);\r
6667 \r
6668     errorDialog = hDlg;\r
6669     SetWindowText(hDlg, errorTitle);\r
6670     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6671     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6672     return FALSE;\r
6673 \r
6674   case WM_COMMAND:\r
6675     switch (LOWORD(wParam)) {\r
6676     case IDOK:\r
6677     case IDCANCEL:\r
6678       if (errorDialog == hDlg) errorDialog = NULL;\r
6679       DestroyWindow(hDlg);\r
6680       return TRUE;\r
6681 \r
6682     default:\r
6683       break;\r
6684     }\r
6685     break;\r
6686   }\r
6687   return FALSE;\r
6688 }\r
6689 \r
6690 #ifdef GOTHIC\r
6691 HWND gothicDialog = NULL;\r
6692 \r
6693 LRESULT CALLBACK\r
6694 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6695 {\r
6696   HANDLE hwndText;\r
6697   RECT rChild;\r
6698   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6699 \r
6700   switch (message) {\r
6701   case WM_INITDIALOG:\r
6702     GetWindowRect(hDlg, &rChild);\r
6703 \r
6704     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6705                                                              SWP_NOZORDER);\r
6706 \r
6707     /* \r
6708         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6709         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6710         and it doesn't work when you resize the dialog.\r
6711         For now, just give it a default position.\r
6712     */\r
6713     gothicDialog = hDlg;\r
6714     SetWindowText(hDlg, errorTitle);\r
6715     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6716     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6717     return FALSE;\r
6718 \r
6719   case WM_COMMAND:\r
6720     switch (LOWORD(wParam)) {\r
6721     case IDOK:\r
6722     case IDCANCEL:\r
6723       if (errorDialog == hDlg) errorDialog = NULL;\r
6724       DestroyWindow(hDlg);\r
6725       return TRUE;\r
6726 \r
6727     default:\r
6728       break;\r
6729     }\r
6730     break;\r
6731   }\r
6732   return FALSE;\r
6733 }\r
6734 \r
6735 VOID\r
6736 GothicPopUp(char *title, VariantClass variant)\r
6737 {\r
6738   FARPROC lpProc;\r
6739   static char *lastTitle;\r
6740 \r
6741   strncpy(errorTitle, title, sizeof(errorTitle));\r
6742   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6743 \r
6744   if(lastTitle != title && gothicDialog != NULL) {\r
6745     DestroyWindow(gothicDialog);\r
6746     gothicDialog = NULL;\r
6747   }\r
6748   if(variant != VariantNormal && gothicDialog == NULL) {\r
6749     title = lastTitle;\r
6750     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6751     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6752                  hwndMain, (DLGPROC)lpProc);\r
6753     FreeProcInstance(lpProc);\r
6754   }\r
6755 }\r
6756 #endif\r
6757 \r
6758 /*---------------------------------------------------------------------------*\\r
6759  *\r
6760  *  Ics Interaction console functions\r
6761  *\r
6762 \*---------------------------------------------------------------------------*/\r
6763 \r
6764 #define HISTORY_SIZE 64\r
6765 static char *history[HISTORY_SIZE];\r
6766 int histIn = 0, histP = 0;\r
6767 \r
6768 VOID\r
6769 SaveInHistory(char *cmd)\r
6770 {\r
6771   if (history[histIn] != NULL) {\r
6772     free(history[histIn]);\r
6773     history[histIn] = NULL;\r
6774   }\r
6775   if (*cmd == NULLCHAR) return;\r
6776   history[histIn] = StrSave(cmd);\r
6777   histIn = (histIn + 1) % HISTORY_SIZE;\r
6778   if (history[histIn] != NULL) {\r
6779     free(history[histIn]);\r
6780     history[histIn] = NULL;\r
6781   }\r
6782   histP = histIn;\r
6783 }\r
6784 \r
6785 char *\r
6786 PrevInHistory(char *cmd)\r
6787 {\r
6788   int newhp;\r
6789   if (histP == histIn) {\r
6790     if (history[histIn] != NULL) free(history[histIn]);\r
6791     history[histIn] = StrSave(cmd);\r
6792   }\r
6793   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6794   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6795   histP = newhp;\r
6796   return history[histP];\r
6797 }\r
6798 \r
6799 char *\r
6800 NextInHistory()\r
6801 {\r
6802   if (histP == histIn) return NULL;\r
6803   histP = (histP + 1) % HISTORY_SIZE;\r
6804   return history[histP];   \r
6805 }\r
6806 \r
6807 HMENU\r
6808 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6809 {\r
6810   HMENU hmenu, h;\r
6811   int i = 0;\r
6812   hmenu = LoadMenu(hInst, "TextMenu");\r
6813   h = GetSubMenu(hmenu, 0);\r
6814   while (e->item) {\r
6815     if (strcmp(e->item, "-") == 0) {\r
6816       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6817     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6818       int flags = MF_STRING, j = 0;\r
6819       if (e->item[0] == '|') {\r
6820         flags |= MF_MENUBARBREAK;\r
6821         j++;\r
6822       }\r
6823       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6824       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6825     }\r
6826     e++;\r
6827     i++;\r
6828   } \r
6829   return hmenu;\r
6830 }\r
6831 \r
6832 WNDPROC consoleTextWindowProc;\r
6833 \r
6834 void\r
6835 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6836 {\r
6837   char buf[MSG_SIZ], name[MSG_SIZ];\r
6838   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6839   CHARRANGE sel;\r
6840 \r
6841   if (!getname) {\r
6842     SetWindowText(hInput, command);\r
6843     if (immediate) {\r
6844       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6845     } else {\r
6846       sel.cpMin = 999999;\r
6847       sel.cpMax = 999999;\r
6848       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6849       SetFocus(hInput);\r
6850     }\r
6851     return;\r
6852   }    \r
6853   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6854   if (sel.cpMin == sel.cpMax) {\r
6855     /* Expand to surrounding word */\r
6856     TEXTRANGE tr;\r
6857     do {\r
6858       tr.chrg.cpMax = sel.cpMin;\r
6859       tr.chrg.cpMin = --sel.cpMin;\r
6860       if (sel.cpMin < 0) break;\r
6861       tr.lpstrText = name;\r
6862       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6863     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6864     sel.cpMin++;\r
6865 \r
6866     do {\r
6867       tr.chrg.cpMin = sel.cpMax;\r
6868       tr.chrg.cpMax = ++sel.cpMax;\r
6869       tr.lpstrText = name;\r
6870       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6871     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6872     sel.cpMax--;\r
6873 \r
6874     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6875       MessageBeep(MB_ICONEXCLAMATION);\r
6876       return;\r
6877     }\r
6878     tr.chrg = sel;\r
6879     tr.lpstrText = name;\r
6880     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6881   } else {\r
6882     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6883       MessageBeep(MB_ICONEXCLAMATION);\r
6884       return;\r
6885     }\r
6886     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6887   }\r
6888   if (immediate) {\r
6889     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6890     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6891     SetWindowText(hInput, buf);\r
6892     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6893   } else {\r
6894     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6895       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6896     SetWindowText(hInput, buf);\r
6897     sel.cpMin = 999999;\r
6898     sel.cpMax = 999999;\r
6899     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6900     SetFocus(hInput);\r
6901   }\r
6902 }\r
6903 \r
6904 LRESULT CALLBACK \r
6905 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6906 {\r
6907   HWND hInput;\r
6908   CHARRANGE sel;\r
6909 \r
6910   switch (message) {\r
6911   case WM_KEYDOWN:\r
6912     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6913     if(wParam=='R') return 0;\r
6914     switch (wParam) {\r
6915     case VK_PRIOR:\r
6916       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6917       return 0;\r
6918     case VK_NEXT:\r
6919       sel.cpMin = 999999;\r
6920       sel.cpMax = 999999;\r
6921       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6922       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6923       return 0;\r
6924     }\r
6925     break;\r
6926   case WM_CHAR:\r
6927    if(wParam != '\022') {\r
6928     if (wParam == '\t') {\r
6929       if (GetKeyState(VK_SHIFT) < 0) {\r
6930         /* shifted */\r
6931         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6932         if (buttonDesc[0].hwnd) {\r
6933           SetFocus(buttonDesc[0].hwnd);\r
6934         } else {\r
6935           SetFocus(hwndMain);\r
6936         }\r
6937       } else {\r
6938         /* unshifted */\r
6939         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6940       }\r
6941     } else {\r
6942       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6943       JAWS_DELETE( SetFocus(hInput); )\r
6944       SendMessage(hInput, message, wParam, lParam);\r
6945     }\r
6946     return 0;\r
6947    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6948    lParam = -1;\r
6949   case WM_RBUTTONDOWN:\r
6950     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6951       /* Move selection here if it was empty */\r
6952       POINT pt;\r
6953       pt.x = LOWORD(lParam);\r
6954       pt.y = HIWORD(lParam);\r
6955       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6956       if (sel.cpMin == sel.cpMax) {\r
6957         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6958         sel.cpMax = sel.cpMin;\r
6959         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6960       }\r
6961       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6962 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6963       POINT pt;\r
6964       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6965       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6966       if (sel.cpMin == sel.cpMax) {\r
6967         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6968         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6969       }\r
6970       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6971         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6972       }\r
6973       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6974       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6975       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6976       MenuPopup(hwnd, pt, hmenu, -1);\r
6977 }\r
6978     }\r
6979     return 0;\r
6980   case WM_RBUTTONUP:\r
6981     if (GetKeyState(VK_SHIFT) & ~1) {\r
6982       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6983         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6984     }\r
6985     return 0;\r
6986   case WM_PASTE:\r
6987     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6988     SetFocus(hInput);\r
6989     return SendMessage(hInput, message, wParam, lParam);\r
6990   case WM_MBUTTONDOWN:\r
6991     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6992   case WM_COMMAND:\r
6993     switch (LOWORD(wParam)) {\r
6994     case IDM_QuickPaste:\r
6995       {\r
6996         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6997         if (sel.cpMin == sel.cpMax) {\r
6998           MessageBeep(MB_ICONEXCLAMATION);\r
6999           return 0;\r
7000         }\r
7001         SendMessage(hwnd, WM_COPY, 0, 0);\r
7002         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7003         SendMessage(hInput, WM_PASTE, 0, 0);\r
7004         SetFocus(hInput);\r
7005         return 0;\r
7006       }\r
7007     case IDM_Cut:\r
7008       SendMessage(hwnd, WM_CUT, 0, 0);\r
7009       return 0;\r
7010     case IDM_Paste:\r
7011       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7012       return 0;\r
7013     case IDM_Copy:\r
7014       SendMessage(hwnd, WM_COPY, 0, 0);\r
7015       return 0;\r
7016     default:\r
7017       {\r
7018         int i = LOWORD(wParam) - IDM_CommandX;\r
7019         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7020             icsTextMenuEntry[i].command != NULL) {\r
7021           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7022                    icsTextMenuEntry[i].getname,\r
7023                    icsTextMenuEntry[i].immediate);\r
7024           return 0;\r
7025         }\r
7026       }\r
7027       break;\r
7028     }\r
7029     break;\r
7030   }\r
7031   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7032 }\r
7033 \r
7034 WNDPROC consoleInputWindowProc;\r
7035 \r
7036 LRESULT CALLBACK\r
7037 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7038 {\r
7039   char buf[MSG_SIZ];\r
7040   char *p;\r
7041   static BOOL sendNextChar = FALSE;\r
7042   static BOOL quoteNextChar = FALSE;\r
7043   InputSource *is = consoleInputSource;\r
7044   CHARFORMAT cf;\r
7045   CHARRANGE sel;\r
7046 \r
7047   switch (message) {\r
7048   case WM_CHAR:\r
7049     if (!appData.localLineEditing || sendNextChar) {\r
7050       is->buf[0] = (CHAR) wParam;\r
7051       is->count = 1;\r
7052       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7053       sendNextChar = FALSE;\r
7054       return 0;\r
7055     }\r
7056     if (quoteNextChar) {\r
7057       buf[0] = (char) wParam;\r
7058       buf[1] = NULLCHAR;\r
7059       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7060       quoteNextChar = FALSE;\r
7061       return 0;\r
7062     }\r
7063     switch (wParam) {\r
7064     case '\r':   /* Enter key */\r
7065       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7066       if (consoleEcho) SaveInHistory(is->buf);\r
7067       is->buf[is->count++] = '\n';\r
7068       is->buf[is->count] = NULLCHAR;\r
7069       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7070       if (consoleEcho) {\r
7071         ConsoleOutput(is->buf, is->count, TRUE);\r
7072       } else if (appData.localLineEditing) {\r
7073         ConsoleOutput("\n", 1, TRUE);\r
7074       }\r
7075       /* fall thru */\r
7076     case '\033': /* Escape key */\r
7077       SetWindowText(hwnd, "");\r
7078       cf.cbSize = sizeof(CHARFORMAT);\r
7079       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7080       if (consoleEcho) {\r
7081         cf.crTextColor = textAttribs[ColorNormal].color;\r
7082       } else {\r
7083         cf.crTextColor = COLOR_ECHOOFF;\r
7084       }\r
7085       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7086       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7087       return 0;\r
7088     case '\t':   /* Tab key */\r
7089       if (GetKeyState(VK_SHIFT) < 0) {\r
7090         /* shifted */\r
7091         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7092       } else {\r
7093         /* unshifted */\r
7094         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7095         if (buttonDesc[0].hwnd) {\r
7096           SetFocus(buttonDesc[0].hwnd);\r
7097         } else {\r
7098           SetFocus(hwndMain);\r
7099         }\r
7100       }\r
7101       return 0;\r
7102     case '\023': /* Ctrl+S */\r
7103       sendNextChar = TRUE;\r
7104       return 0;\r
7105     case '\021': /* Ctrl+Q */\r
7106       quoteNextChar = TRUE;\r
7107       return 0;\r
7108     JAWS_REPLAY\r
7109     default:\r
7110       break;\r
7111     }\r
7112     break;\r
7113   case WM_KEYDOWN:\r
7114     switch (wParam) {\r
7115     case VK_UP:\r
7116       GetWindowText(hwnd, buf, MSG_SIZ);\r
7117       p = PrevInHistory(buf);\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_DOWN:\r
7127       p = NextInHistory();\r
7128       if (p != NULL) {\r
7129         SetWindowText(hwnd, p);\r
7130         sel.cpMin = 999999;\r
7131         sel.cpMax = 999999;\r
7132         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7133         return 0;\r
7134       }\r
7135       break;\r
7136     case VK_HOME:\r
7137     case VK_END:\r
7138       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7139       /* fall thru */\r
7140     case VK_PRIOR:\r
7141     case VK_NEXT:\r
7142       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7143       return 0;\r
7144     }\r
7145     break;\r
7146   case WM_MBUTTONDOWN:\r
7147     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7148       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7149     break;\r
7150   case WM_RBUTTONUP:\r
7151     if (GetKeyState(VK_SHIFT) & ~1) {\r
7152       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7153         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7154     } else {\r
7155       POINT pt;\r
7156       HMENU hmenu;\r
7157       hmenu = LoadMenu(hInst, "InputMenu");\r
7158       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7159       if (sel.cpMin == sel.cpMax) {\r
7160         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7161         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7162       }\r
7163       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7164         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7165       }\r
7166       pt.x = LOWORD(lParam);\r
7167       pt.y = HIWORD(lParam);\r
7168       MenuPopup(hwnd, pt, hmenu, -1);\r
7169     }\r
7170     return 0;\r
7171   case WM_COMMAND:\r
7172     switch (LOWORD(wParam)) { \r
7173     case IDM_Undo:\r
7174       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7175       return 0;\r
7176     case IDM_SelectAll:\r
7177       sel.cpMin = 0;\r
7178       sel.cpMax = -1; /*999999?*/\r
7179       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7180       return 0;\r
7181     case IDM_Cut:\r
7182       SendMessage(hwnd, WM_CUT, 0, 0);\r
7183       return 0;\r
7184     case IDM_Paste:\r
7185       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7186       return 0;\r
7187     case IDM_Copy:\r
7188       SendMessage(hwnd, WM_COPY, 0, 0);\r
7189       return 0;\r
7190     }\r
7191     break;\r
7192   }\r
7193   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7194 }\r
7195 \r
7196 #define CO_MAX  100000\r
7197 #define CO_TRIM   1000\r
7198 \r
7199 LRESULT CALLBACK\r
7200 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7201 {\r
7202   static SnapData sd;\r
7203   HWND hText, hInput;\r
7204   RECT rect;\r
7205   static int sizeX, sizeY;\r
7206   int newSizeX, newSizeY;\r
7207   MINMAXINFO *mmi;\r
7208   WORD wMask;\r
7209 \r
7210   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7211   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7212 \r
7213   switch (message) {\r
7214   case WM_NOTIFY:\r
7215     if (((NMHDR*)lParam)->code == EN_LINK)\r
7216     {\r
7217       ENLINK *pLink = (ENLINK*)lParam;\r
7218       if (pLink->msg == WM_LBUTTONUP)\r
7219       {\r
7220         TEXTRANGE tr;\r
7221 \r
7222         tr.chrg = pLink->chrg;\r
7223         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7224         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7225         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7226         free(tr.lpstrText);\r
7227       }\r
7228     }\r
7229     break;\r
7230   case WM_INITDIALOG: /* message: initialize dialog box */\r
7231     hwndConsole = hDlg;\r
7232     SetFocus(hInput);\r
7233     consoleTextWindowProc = (WNDPROC)\r
7234       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7235     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7236     consoleInputWindowProc = (WNDPROC)\r
7237       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7238     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7239     Colorize(ColorNormal, TRUE);\r
7240     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7241     ChangedConsoleFont();\r
7242     GetClientRect(hDlg, &rect);\r
7243     sizeX = rect.right;\r
7244     sizeY = rect.bottom;\r
7245     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7246         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7247       WINDOWPLACEMENT wp;\r
7248       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7249       wp.length = sizeof(WINDOWPLACEMENT);\r
7250       wp.flags = 0;\r
7251       wp.showCmd = SW_SHOW;\r
7252       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7253       wp.rcNormalPosition.left = wpConsole.x;\r
7254       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7255       wp.rcNormalPosition.top = wpConsole.y;\r
7256       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7257       SetWindowPlacement(hDlg, &wp);\r
7258     }\r
7259 \r
7260    // [HGM] Chessknight's change 2004-07-13\r
7261    else { /* Determine Defaults */\r
7262        WINDOWPLACEMENT wp;\r
7263        wpConsole.x = wpMain.width + 1;\r
7264        wpConsole.y = wpMain.y;\r
7265        wpConsole.width = screenWidth -  wpMain.width;\r
7266        wpConsole.height = wpMain.height;\r
7267        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7268        wp.length = sizeof(WINDOWPLACEMENT);\r
7269        wp.flags = 0;\r
7270        wp.showCmd = SW_SHOW;\r
7271        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7272        wp.rcNormalPosition.left = wpConsole.x;\r
7273        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7274        wp.rcNormalPosition.top = wpConsole.y;\r
7275        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7276        SetWindowPlacement(hDlg, &wp);\r
7277     }\r
7278 \r
7279    // Allow hText to highlight URLs and send notifications on them\r
7280    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7281    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7282    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7283    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7284 \r
7285     return FALSE;\r
7286 \r
7287   case WM_SETFOCUS:\r
7288     SetFocus(hInput);\r
7289     return 0;\r
7290 \r
7291   case WM_CLOSE:\r
7292     ExitEvent(0);\r
7293     /* not reached */\r
7294     break;\r
7295 \r
7296   case WM_SIZE:\r
7297     if (IsIconic(hDlg)) break;\r
7298     newSizeX = LOWORD(lParam);\r
7299     newSizeY = HIWORD(lParam);\r
7300     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7301       RECT rectText, rectInput;\r
7302       POINT pt;\r
7303       int newTextHeight, newTextWidth;\r
7304       GetWindowRect(hText, &rectText);\r
7305       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7306       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7307       if (newTextHeight < 0) {\r
7308         newSizeY += -newTextHeight;\r
7309         newTextHeight = 0;\r
7310       }\r
7311       SetWindowPos(hText, NULL, 0, 0,\r
7312         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7313       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7314       pt.x = rectInput.left;\r
7315       pt.y = rectInput.top + newSizeY - sizeY;\r
7316       ScreenToClient(hDlg, &pt);\r
7317       SetWindowPos(hInput, NULL, \r
7318         pt.x, pt.y, /* needs client coords */   \r
7319         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7320         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7321     }\r
7322     sizeX = newSizeX;\r
7323     sizeY = newSizeY;\r
7324     break;\r
7325 \r
7326   case WM_GETMINMAXINFO:\r
7327     /* Prevent resizing window too small */\r
7328     mmi = (MINMAXINFO *) lParam;\r
7329     mmi->ptMinTrackSize.x = 100;\r
7330     mmi->ptMinTrackSize.y = 100;\r
7331     break;\r
7332 \r
7333   /* [AS] Snapping */\r
7334   case WM_ENTERSIZEMOVE:\r
7335     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7336 \r
7337   case WM_SIZING:\r
7338     return OnSizing( &sd, hDlg, wParam, lParam );\r
7339 \r
7340   case WM_MOVING:\r
7341     return OnMoving( &sd, hDlg, wParam, lParam );\r
7342 \r
7343   case WM_EXITSIZEMOVE:\r
7344         UpdateICSWidth(hText);\r
7345     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7346   }\r
7347 \r
7348   return DefWindowProc(hDlg, message, wParam, lParam);\r
7349 }\r
7350 \r
7351 \r
7352 VOID\r
7353 ConsoleCreate()\r
7354 {\r
7355   HWND hCons;\r
7356   if (hwndConsole) return;\r
7357   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7358   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7359 }\r
7360 \r
7361 \r
7362 VOID\r
7363 ConsoleOutput(char* data, int length, int forceVisible)\r
7364 {\r
7365   HWND hText;\r
7366   int trim, exlen;\r
7367   char *p, *q;\r
7368   char buf[CO_MAX+1];\r
7369   POINT pEnd;\r
7370   RECT rect;\r
7371   static int delayLF = 0;\r
7372   CHARRANGE savesel, sel;\r
7373 \r
7374   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7375   p = data;\r
7376   q = buf;\r
7377   if (delayLF) {\r
7378     *q++ = '\r';\r
7379     *q++ = '\n';\r
7380     delayLF = 0;\r
7381   }\r
7382   while (length--) {\r
7383     if (*p == '\n') {\r
7384       if (*++p) {\r
7385         *q++ = '\r';\r
7386         *q++ = '\n';\r
7387       } else {\r
7388         delayLF = 1;\r
7389       }\r
7390     } else if (*p == '\007') {\r
7391        MyPlaySound(&sounds[(int)SoundBell]);\r
7392        p++;\r
7393     } else {\r
7394       *q++ = *p++;\r
7395     }\r
7396   }\r
7397   *q = NULLCHAR;\r
7398   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7399   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7400   /* Save current selection */\r
7401   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7402   exlen = GetWindowTextLength(hText);\r
7403   /* Find out whether current end of text is visible */\r
7404   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7405   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7406   /* Trim existing text if it's too long */\r
7407   if (exlen + (q - buf) > CO_MAX) {\r
7408     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7409     sel.cpMin = 0;\r
7410     sel.cpMax = trim;\r
7411     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7412     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7413     exlen -= trim;\r
7414     savesel.cpMin -= trim;\r
7415     savesel.cpMax -= trim;\r
7416     if (exlen < 0) exlen = 0;\r
7417     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7418     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7419   }\r
7420   /* Append the new text */\r
7421   sel.cpMin = exlen;\r
7422   sel.cpMax = exlen;\r
7423   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7424   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7425   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7426   if (forceVisible || exlen == 0 ||\r
7427       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7428        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7429     /* Scroll to make new end of text visible if old end of text\r
7430        was visible or 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     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7435     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7436     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7437   }\r
7438   if (savesel.cpMax == exlen || forceVisible) {\r
7439     /* Move insert point to new end of text if it was at the old\r
7440        end of text or if the new text is an echo of user typein */\r
7441     sel.cpMin = 9999999;\r
7442     sel.cpMax = 9999999;\r
7443     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7444   } else {\r
7445     /* Restore previous selection */\r
7446     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7447   }\r
7448   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7449 }\r
7450 \r
7451 /*---------*/\r
7452 \r
7453 \r
7454 void\r
7455 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7456 {\r
7457   char buf[100];\r
7458   char *str;\r
7459   COLORREF oldFg, oldBg;\r
7460   HFONT oldFont;\r
7461   RECT rect;\r
7462 \r
7463   if(copyNumber > 1)\r
7464     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7465 \r
7466   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7467   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7468   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7469 \r
7470   rect.left = x;\r
7471   rect.right = x + squareSize;\r
7472   rect.top  = y;\r
7473   rect.bottom = y + squareSize;\r
7474   str = buf;\r
7475 \r
7476   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7477                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7478              y, ETO_CLIPPED|ETO_OPAQUE,\r
7479              &rect, str, strlen(str), NULL);\r
7480 \r
7481   (void) SetTextColor(hdc, oldFg);\r
7482   (void) SetBkColor(hdc, oldBg);\r
7483   (void) SelectObject(hdc, oldFont);\r
7484 }\r
7485 \r
7486 void\r
7487 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7488               RECT *rect, char *color, char *flagFell)\r
7489 {\r
7490   char buf[100];\r
7491   char *str;\r
7492   COLORREF oldFg, oldBg;\r
7493   HFONT oldFont;\r
7494 \r
7495   if (appData.clockMode) {\r
7496     if (tinyLayout)\r
7497       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7498     else\r
7499       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7500     str = buf;\r
7501   } else {\r
7502     str = color;\r
7503   }\r
7504 \r
7505   if (highlight) {\r
7506     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7507     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7508   } else {\r
7509     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7510     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7511   }\r
7512   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7513 \r
7514   JAWS_SILENCE\r
7515 \r
7516   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7517              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7518              rect, str, strlen(str), NULL);\r
7519   if(logoHeight > 0 && appData.clockMode) {\r
7520       RECT r;\r
7521       str += strlen(color)+2;\r
7522       r.top = rect->top + logoHeight/2;\r
7523       r.left = rect->left;\r
7524       r.right = rect->right;\r
7525       r.bottom = rect->bottom;\r
7526       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7527                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7528                  &r, str, strlen(str), NULL);\r
7529   }\r
7530   (void) SetTextColor(hdc, oldFg);\r
7531   (void) SetBkColor(hdc, oldBg);\r
7532   (void) SelectObject(hdc, oldFont);\r
7533 }\r
7534 \r
7535 \r
7536 int\r
7537 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7538            OVERLAPPED *ovl)\r
7539 {\r
7540   int ok, err;\r
7541 \r
7542   /* [AS]  */\r
7543   if( count <= 0 ) {\r
7544     if (appData.debugMode) {\r
7545       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7546     }\r
7547 \r
7548     return ERROR_INVALID_USER_BUFFER;\r
7549   }\r
7550 \r
7551   ResetEvent(ovl->hEvent);\r
7552   ovl->Offset = ovl->OffsetHigh = 0;\r
7553   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7554   if (ok) {\r
7555     err = NO_ERROR;\r
7556   } else {\r
7557     err = GetLastError();\r
7558     if (err == ERROR_IO_PENDING) {\r
7559       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7560       if (ok)\r
7561         err = NO_ERROR;\r
7562       else\r
7563         err = GetLastError();\r
7564     }\r
7565   }\r
7566   return err;\r
7567 }\r
7568 \r
7569 int\r
7570 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7571             OVERLAPPED *ovl)\r
7572 {\r
7573   int ok, err;\r
7574 \r
7575   ResetEvent(ovl->hEvent);\r
7576   ovl->Offset = ovl->OffsetHigh = 0;\r
7577   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7578   if (ok) {\r
7579     err = NO_ERROR;\r
7580   } else {\r
7581     err = GetLastError();\r
7582     if (err == ERROR_IO_PENDING) {\r
7583       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7584       if (ok)\r
7585         err = NO_ERROR;\r
7586       else\r
7587         err = GetLastError();\r
7588     }\r
7589   }\r
7590   return err;\r
7591 }\r
7592 \r
7593 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7594 void CheckForInputBufferFull( InputSource * is )\r
7595 {\r
7596     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7597         /* Look for end of line */\r
7598         char * p = is->buf;\r
7599         \r
7600         while( p < is->next && *p != '\n' ) {\r
7601             p++;\r
7602         }\r
7603 \r
7604         if( p >= is->next ) {\r
7605             if (appData.debugMode) {\r
7606                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7607             }\r
7608 \r
7609             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7610             is->count = (DWORD) -1;\r
7611             is->next = is->buf;\r
7612         }\r
7613     }\r
7614 }\r
7615 \r
7616 DWORD\r
7617 InputThread(LPVOID arg)\r
7618 {\r
7619   InputSource *is;\r
7620   OVERLAPPED ovl;\r
7621 \r
7622   is = (InputSource *) arg;\r
7623   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7624   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7625   while (is->hThread != NULL) {\r
7626     is->error = DoReadFile(is->hFile, is->next,\r
7627                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7628                            &is->count, &ovl);\r
7629     if (is->error == NO_ERROR) {\r
7630       is->next += is->count;\r
7631     } else {\r
7632       if (is->error == ERROR_BROKEN_PIPE) {\r
7633         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7634         is->count = 0;\r
7635       } else {\r
7636         is->count = (DWORD) -1;\r
7637         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7638         break; \r
7639       }\r
7640     }\r
7641 \r
7642     CheckForInputBufferFull( is );\r
7643 \r
7644     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7645 \r
7646     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7647 \r
7648     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7649   }\r
7650 \r
7651   CloseHandle(ovl.hEvent);\r
7652   CloseHandle(is->hFile);\r
7653 \r
7654   if (appData.debugMode) {\r
7655     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7656   }\r
7657 \r
7658   return 0;\r
7659 }\r
7660 \r
7661 \r
7662 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7663 DWORD\r
7664 NonOvlInputThread(LPVOID arg)\r
7665 {\r
7666   InputSource *is;\r
7667   char *p, *q;\r
7668   int i;\r
7669   char prev;\r
7670 \r
7671   is = (InputSource *) arg;\r
7672   while (is->hThread != NULL) {\r
7673     is->error = ReadFile(is->hFile, is->next,\r
7674                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7675                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7676     if (is->error == NO_ERROR) {\r
7677       /* Change CRLF to LF */\r
7678       if (is->next > is->buf) {\r
7679         p = is->next - 1;\r
7680         i = is->count + 1;\r
7681       } else {\r
7682         p = is->next;\r
7683         i = is->count;\r
7684       }\r
7685       q = p;\r
7686       prev = NULLCHAR;\r
7687       while (i > 0) {\r
7688         if (prev == '\r' && *p == '\n') {\r
7689           *(q-1) = '\n';\r
7690           is->count--;\r
7691         } else { \r
7692           *q++ = *p;\r
7693         }\r
7694         prev = *p++;\r
7695         i--;\r
7696       }\r
7697       *q = NULLCHAR;\r
7698       is->next = q;\r
7699     } else {\r
7700       if (is->error == ERROR_BROKEN_PIPE) {\r
7701         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7702         is->count = 0; \r
7703       } else {\r
7704         is->count = (DWORD) -1;\r
7705       }\r
7706     }\r
7707 \r
7708     CheckForInputBufferFull( is );\r
7709 \r
7710     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7711 \r
7712     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7713 \r
7714     if (is->count < 0) break;  /* Quit on error */\r
7715   }\r
7716   CloseHandle(is->hFile);\r
7717   return 0;\r
7718 }\r
7719 \r
7720 DWORD\r
7721 SocketInputThread(LPVOID arg)\r
7722 {\r
7723   InputSource *is;\r
7724 \r
7725   is = (InputSource *) arg;\r
7726   while (is->hThread != NULL) {\r
7727     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7728     if ((int)is->count == SOCKET_ERROR) {\r
7729       is->count = (DWORD) -1;\r
7730       is->error = WSAGetLastError();\r
7731     } else {\r
7732       is->error = NO_ERROR;\r
7733       is->next += is->count;\r
7734       if (is->count == 0 && is->second == is) {\r
7735         /* End of file on stderr; quit with no message */\r
7736         break;\r
7737       }\r
7738     }\r
7739     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7740 \r
7741     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7742 \r
7743     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7744   }\r
7745   return 0;\r
7746 }\r
7747 \r
7748 VOID\r
7749 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7750 {\r
7751   InputSource *is;\r
7752 \r
7753   is = (InputSource *) lParam;\r
7754   if (is->lineByLine) {\r
7755     /* Feed in lines one by one */\r
7756     char *p = is->buf;\r
7757     char *q = p;\r
7758     while (q < is->next) {\r
7759       if (*q++ == '\n') {\r
7760         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7761         p = q;\r
7762       }\r
7763     }\r
7764     \r
7765     /* Move any partial line to the start of the buffer */\r
7766     q = is->buf;\r
7767     while (p < is->next) {\r
7768       *q++ = *p++;\r
7769     }\r
7770     is->next = q;\r
7771 \r
7772     if (is->error != NO_ERROR || is->count == 0) {\r
7773       /* Notify backend of the error.  Note: If there was a partial\r
7774          line at the end, it is not flushed through. */\r
7775       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7776     }\r
7777   } else {\r
7778     /* Feed in the whole chunk of input at once */\r
7779     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7780     is->next = is->buf;\r
7781   }\r
7782 }\r
7783 \r
7784 /*---------------------------------------------------------------------------*\\r
7785  *\r
7786  *  Menu enables. Used when setting various modes.\r
7787  *\r
7788 \*---------------------------------------------------------------------------*/\r
7789 \r
7790 typedef struct {\r
7791   int item;\r
7792   int flags;\r
7793 } Enables;\r
7794 \r
7795 VOID\r
7796 GreyRevert(Boolean grey)\r
7797 { // [HGM] vari: for retracting variations in local mode\r
7798   HMENU hmenu = GetMenu(hwndMain);\r
7799   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7800   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7801 }\r
7802 \r
7803 VOID\r
7804 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7805 {\r
7806   while (enab->item > 0) {\r
7807     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7808     enab++;\r
7809   }\r
7810 }\r
7811 \r
7812 Enables gnuEnables[] = {\r
7813   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7826 \r
7827   // Needed to switch from ncp to GNU mode on Engine Load\r
7828   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7829   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7833   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7835   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7836   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7837   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7842   { -1, -1 }\r
7843 };\r
7844 \r
7845 Enables icsEnables[] = {\r
7846   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7865   { -1, -1 }\r
7866 };\r
7867 \r
7868 #if ZIPPY\r
7869 Enables zippyEnables[] = {\r
7870   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7874   { -1, -1 }\r
7875 };\r
7876 #endif\r
7877 \r
7878 Enables ncpEnables[] = {\r
7879   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7888   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7889   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7901   { -1, -1 }\r
7902 };\r
7903 \r
7904 Enables trainingOnEnables[] = {\r
7905   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7914   { -1, -1 }\r
7915 };\r
7916 \r
7917 Enables trainingOffEnables[] = {\r
7918   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7927   { -1, -1 }\r
7928 };\r
7929 \r
7930 /* These modify either ncpEnables or gnuEnables */\r
7931 Enables cmailEnables[] = {\r
7932   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7934   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7935   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7939   { -1, -1 }\r
7940 };\r
7941 \r
7942 Enables machineThinkingEnables[] = {\r
7943   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7956 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7959   { -1, -1 }\r
7960 };\r
7961 \r
7962 Enables userThinkingEnables[] = {\r
7963   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7964   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7965   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7967   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7968   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7969   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7971   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7976 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7979   { -1, -1 }\r
7980 };\r
7981 \r
7982 /*---------------------------------------------------------------------------*\\r
7983  *\r
7984  *  Front-end interface functions exported by XBoard.\r
7985  *  Functions appear in same order as prototypes in frontend.h.\r
7986  * \r
7987 \*---------------------------------------------------------------------------*/\r
7988 VOID\r
7989 CheckMark(UINT item, int state)\r
7990 {\r
7991     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7992 }\r
7993 \r
7994 VOID\r
7995 ModeHighlight()\r
7996 {\r
7997   static UINT prevChecked = 0;\r
7998   static int prevPausing = 0;\r
7999   UINT nowChecked;\r
8000 \r
8001   if (pausing != prevPausing) {\r
8002     prevPausing = pausing;\r
8003     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8004                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8005     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8006   }\r
8007 \r
8008   switch (gameMode) {\r
8009   case BeginningOfGame:\r
8010     if (appData.icsActive)\r
8011       nowChecked = IDM_IcsClient;\r
8012     else if (appData.noChessProgram)\r
8013       nowChecked = IDM_EditGame;\r
8014     else\r
8015       nowChecked = IDM_MachineBlack;\r
8016     break;\r
8017   case MachinePlaysBlack:\r
8018     nowChecked = IDM_MachineBlack;\r
8019     break;\r
8020   case MachinePlaysWhite:\r
8021     nowChecked = IDM_MachineWhite;\r
8022     break;\r
8023   case TwoMachinesPlay:\r
8024     nowChecked = IDM_TwoMachines;\r
8025     break;\r
8026   case AnalyzeMode:\r
8027     nowChecked = IDM_AnalysisMode;\r
8028     break;\r
8029   case AnalyzeFile:\r
8030     nowChecked = IDM_AnalyzeFile;\r
8031     break;\r
8032   case EditGame:\r
8033     nowChecked = IDM_EditGame;\r
8034     break;\r
8035   case PlayFromGameFile:\r
8036     nowChecked = IDM_LoadGame;\r
8037     break;\r
8038   case EditPosition:\r
8039     nowChecked = IDM_EditPosition;\r
8040     break;\r
8041   case Training:\r
8042     nowChecked = IDM_Training;\r
8043     break;\r
8044   case IcsPlayingWhite:\r
8045   case IcsPlayingBlack:\r
8046   case IcsObserving:\r
8047   case IcsIdle:\r
8048     nowChecked = IDM_IcsClient;\r
8049     break;\r
8050   default:\r
8051   case EndOfGame:\r
8052     nowChecked = 0;\r
8053     break;\r
8054   }\r
8055   CheckMark(prevChecked, MF_UNCHECKED);\r
8056   CheckMark(nowChecked, MF_CHECKED);\r
8057   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8058 \r
8059   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8060     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8061                           MF_BYCOMMAND|MF_ENABLED);\r
8062   } else {\r
8063     (void) EnableMenuItem(GetMenu(hwndMain), \r
8064                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8065   }\r
8066 \r
8067   prevChecked = nowChecked;\r
8068 \r
8069   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8070   if (appData.icsActive) {\r
8071        if (appData.icsEngineAnalyze) {\r
8072                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8073        } else {\r
8074                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8075        }\r
8076   }\r
8077   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8078 }\r
8079 \r
8080 VOID\r
8081 SetICSMode()\r
8082 {\r
8083   HMENU hmenu = GetMenu(hwndMain);\r
8084   SetMenuEnables(hmenu, icsEnables);\r
8085   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8086     MF_BYCOMMAND|MF_ENABLED);\r
8087 #if ZIPPY\r
8088   if (appData.zippyPlay) {\r
8089     SetMenuEnables(hmenu, zippyEnables);\r
8090     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8091          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8092           MF_BYCOMMAND|MF_ENABLED);\r
8093   }\r
8094 #endif\r
8095 }\r
8096 \r
8097 VOID\r
8098 SetGNUMode()\r
8099 {\r
8100   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8101 }\r
8102 \r
8103 VOID\r
8104 SetNCPMode()\r
8105 {\r
8106   HMENU hmenu = GetMenu(hwndMain);\r
8107   SetMenuEnables(hmenu, ncpEnables);\r
8108     DrawMenuBar(hwndMain);\r
8109 }\r
8110 \r
8111 VOID\r
8112 SetCmailMode()\r
8113 {\r
8114   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8115 }\r
8116 \r
8117 VOID \r
8118 SetTrainingModeOn()\r
8119 {\r
8120   int i;\r
8121   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8122   for (i = 0; i < N_BUTTONS; i++) {\r
8123     if (buttonDesc[i].hwnd != NULL)\r
8124       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8125   }\r
8126   CommentPopDown();\r
8127 }\r
8128 \r
8129 VOID SetTrainingModeOff()\r
8130 {\r
8131   int i;\r
8132   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8133   for (i = 0; i < N_BUTTONS; i++) {\r
8134     if (buttonDesc[i].hwnd != NULL)\r
8135       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8136   }\r
8137 }\r
8138 \r
8139 \r
8140 VOID\r
8141 SetUserThinkingEnables()\r
8142 {\r
8143   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8144 }\r
8145 \r
8146 VOID\r
8147 SetMachineThinkingEnables()\r
8148 {\r
8149   HMENU hMenu = GetMenu(hwndMain);\r
8150   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8151 \r
8152   SetMenuEnables(hMenu, machineThinkingEnables);\r
8153 \r
8154   if (gameMode == MachinePlaysBlack) {\r
8155     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8156   } else if (gameMode == MachinePlaysWhite) {\r
8157     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8158   } else if (gameMode == TwoMachinesPlay) {\r
8159     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8160   }\r
8161 }\r
8162 \r
8163 \r
8164 VOID\r
8165 DisplayTitle(char *str)\r
8166 {\r
8167   char title[MSG_SIZ], *host;\r
8168   if (str[0] != NULLCHAR) {\r
8169     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8170   } else if (appData.icsActive) {\r
8171     if (appData.icsCommPort[0] != NULLCHAR)\r
8172       host = "ICS";\r
8173     else \r
8174       host = appData.icsHost;\r
8175       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8176   } else if (appData.noChessProgram) {\r
8177     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8178   } else {\r
8179     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8180     strcat(title, ": ");\r
8181     strcat(title, first.tidy);\r
8182   }\r
8183   SetWindowText(hwndMain, title);\r
8184 }\r
8185 \r
8186 \r
8187 VOID\r
8188 DisplayMessage(char *str1, char *str2)\r
8189 {\r
8190   HDC hdc;\r
8191   HFONT oldFont;\r
8192   int remain = MESSAGE_TEXT_MAX - 1;\r
8193   int len;\r
8194 \r
8195   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8196   messageText[0] = NULLCHAR;\r
8197   if (*str1) {\r
8198     len = strlen(str1);\r
8199     if (len > remain) len = remain;\r
8200     strncpy(messageText, str1, len);\r
8201     messageText[len] = NULLCHAR;\r
8202     remain -= len;\r
8203   }\r
8204   if (*str2 && remain >= 2) {\r
8205     if (*str1) {\r
8206       strcat(messageText, "  ");\r
8207       remain -= 2;\r
8208     }\r
8209     len = strlen(str2);\r
8210     if (len > remain) len = remain;\r
8211     strncat(messageText, str2, len);\r
8212   }\r
8213   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8214   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8215 \r
8216   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8217 \r
8218   SAYMACHINEMOVE();\r
8219 \r
8220   hdc = GetDC(hwndMain);\r
8221   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8222   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8223              &messageRect, messageText, strlen(messageText), NULL);\r
8224   (void) SelectObject(hdc, oldFont);\r
8225   (void) ReleaseDC(hwndMain, hdc);\r
8226 }\r
8227 \r
8228 VOID\r
8229 DisplayError(char *str, int error)\r
8230 {\r
8231   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8232   int len;\r
8233 \r
8234   if (error == 0) {\r
8235     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8236   } else {\r
8237     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8238                         NULL, error, LANG_NEUTRAL,\r
8239                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8240     if (len > 0) {\r
8241       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8242     } else {\r
8243       ErrorMap *em = errmap;\r
8244       while (em->err != 0 && em->err != error) em++;\r
8245       if (em->err != 0) {\r
8246         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8247       } else {\r
8248         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8249       }\r
8250     }\r
8251   }\r
8252   \r
8253   ErrorPopUp(_("Error"), buf);\r
8254 }\r
8255 \r
8256 \r
8257 VOID\r
8258 DisplayMoveError(char *str)\r
8259 {\r
8260   fromX = fromY = -1;\r
8261   ClearHighlights();\r
8262   DrawPosition(FALSE, NULL);\r
8263   if (appData.popupMoveErrors) {\r
8264     ErrorPopUp(_("Error"), str);\r
8265   } else {\r
8266     DisplayMessage(str, "");\r
8267     moveErrorMessageUp = TRUE;\r
8268   }\r
8269 }\r
8270 \r
8271 VOID\r
8272 DisplayFatalError(char *str, int error, int exitStatus)\r
8273 {\r
8274   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8275   int len;\r
8276   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8277 \r
8278   if (error != 0) {\r
8279     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8280                         NULL, error, LANG_NEUTRAL,\r
8281                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8282     if (len > 0) {\r
8283       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8284     } else {\r
8285       ErrorMap *em = errmap;\r
8286       while (em->err != 0 && em->err != error) em++;\r
8287       if (em->err != 0) {\r
8288         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8289       } else {\r
8290         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8291       }\r
8292     }\r
8293     str = buf;\r
8294   }\r
8295   if (appData.debugMode) {\r
8296     fprintf(debugFP, "%s: %s\n", label, str);\r
8297   }\r
8298   if (appData.popupExitMessage) {\r
8299     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8300                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8301   }\r
8302   ExitEvent(exitStatus);\r
8303 }\r
8304 \r
8305 \r
8306 VOID\r
8307 DisplayInformation(char *str)\r
8308 {\r
8309   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8310 }\r
8311 \r
8312 \r
8313 VOID\r
8314 DisplayNote(char *str)\r
8315 {\r
8316   ErrorPopUp(_("Note"), str);\r
8317 }\r
8318 \r
8319 \r
8320 typedef struct {\r
8321   char *title, *question, *replyPrefix;\r
8322   ProcRef pr;\r
8323 } QuestionParams;\r
8324 \r
8325 LRESULT CALLBACK\r
8326 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8327 {\r
8328   static QuestionParams *qp;\r
8329   char reply[MSG_SIZ];\r
8330   int len, err;\r
8331 \r
8332   switch (message) {\r
8333   case WM_INITDIALOG:\r
8334     qp = (QuestionParams *) lParam;\r
8335     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8336     Translate(hDlg, DLG_Question);\r
8337     SetWindowText(hDlg, qp->title);\r
8338     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8339     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8340     return FALSE;\r
8341 \r
8342   case WM_COMMAND:\r
8343     switch (LOWORD(wParam)) {\r
8344     case IDOK:\r
8345       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8346       if (*reply) strcat(reply, " ");\r
8347       len = strlen(reply);\r
8348       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8349       strcat(reply, "\n");\r
8350       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8351       EndDialog(hDlg, TRUE);\r
8352       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8353       return TRUE;\r
8354     case IDCANCEL:\r
8355       EndDialog(hDlg, FALSE);\r
8356       return TRUE;\r
8357     default:\r
8358       break;\r
8359     }\r
8360     break;\r
8361   }\r
8362   return FALSE;\r
8363 }\r
8364 \r
8365 VOID\r
8366 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8367 {\r
8368     QuestionParams qp;\r
8369     FARPROC lpProc;\r
8370     \r
8371     qp.title = title;\r
8372     qp.question = question;\r
8373     qp.replyPrefix = replyPrefix;\r
8374     qp.pr = pr;\r
8375     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8376     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8377       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8378     FreeProcInstance(lpProc);\r
8379 }\r
8380 \r
8381 /* [AS] Pick FRC position */\r
8382 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8383 {\r
8384     static int * lpIndexFRC;\r
8385     BOOL index_is_ok;\r
8386     char buf[16];\r
8387 \r
8388     switch( message )\r
8389     {\r
8390     case WM_INITDIALOG:\r
8391         lpIndexFRC = (int *) lParam;\r
8392 \r
8393         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8394         Translate(hDlg, DLG_NewGameFRC);\r
8395 \r
8396         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8397         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8398         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8399         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8400 \r
8401         break;\r
8402 \r
8403     case WM_COMMAND:\r
8404         switch( LOWORD(wParam) ) {\r
8405         case IDOK:\r
8406             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8407             EndDialog( hDlg, 0 );\r
8408             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8409             return TRUE;\r
8410         case IDCANCEL:\r
8411             EndDialog( hDlg, 1 );   \r
8412             return TRUE;\r
8413         case IDC_NFG_Edit:\r
8414             if( HIWORD(wParam) == EN_CHANGE ) {\r
8415                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8416 \r
8417                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8418             }\r
8419             return TRUE;\r
8420         case IDC_NFG_Random:\r
8421           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8422             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8423             return TRUE;\r
8424         }\r
8425 \r
8426         break;\r
8427     }\r
8428 \r
8429     return FALSE;\r
8430 }\r
8431 \r
8432 int NewGameFRC()\r
8433 {\r
8434     int result;\r
8435     int index = appData.defaultFrcPosition;\r
8436     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8437 \r
8438     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8439 \r
8440     if( result == 0 ) {\r
8441         appData.defaultFrcPosition = index;\r
8442     }\r
8443 \r
8444     return result;\r
8445 }\r
8446 \r
8447 /* [AS] Game list options. Refactored by HGM */\r
8448 \r
8449 HWND gameListOptionsDialog;\r
8450 \r
8451 // low-level front-end: clear text edit / list widget\r
8452 void\r
8453 GLT_ClearList()\r
8454 {\r
8455     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8456 }\r
8457 \r
8458 // low-level front-end: clear text edit / list widget\r
8459 void\r
8460 GLT_DeSelectList()\r
8461 {\r
8462     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8463 }\r
8464 \r
8465 // low-level front-end: append line to text edit / list widget\r
8466 void\r
8467 GLT_AddToList( char *name )\r
8468 {\r
8469     if( name != 0 ) {\r
8470             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8471     }\r
8472 }\r
8473 \r
8474 // low-level front-end: get line from text edit / list widget\r
8475 Boolean\r
8476 GLT_GetFromList( int index, char *name )\r
8477 {\r
8478     if( name != 0 ) {\r
8479             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8480                 return TRUE;\r
8481     }\r
8482     return FALSE;\r
8483 }\r
8484 \r
8485 void GLT_MoveSelection( HWND hDlg, int delta )\r
8486 {\r
8487     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8488     int idx2 = idx1 + delta;\r
8489     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8490 \r
8491     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8492         char buf[128];\r
8493 \r
8494         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8495         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8496         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8497         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8498     }\r
8499 }\r
8500 \r
8501 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8502 {\r
8503     switch( message )\r
8504     {\r
8505     case WM_INITDIALOG:\r
8506         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8507         \r
8508         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8509         Translate(hDlg, DLG_GameListOptions);\r
8510 \r
8511         /* Initialize list */\r
8512         GLT_TagsToList( lpUserGLT );\r
8513 \r
8514         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8515 \r
8516         break;\r
8517 \r
8518     case WM_COMMAND:\r
8519         switch( LOWORD(wParam) ) {\r
8520         case IDOK:\r
8521             GLT_ParseList();\r
8522             EndDialog( hDlg, 0 );\r
8523             return TRUE;\r
8524         case IDCANCEL:\r
8525             EndDialog( hDlg, 1 );\r
8526             return TRUE;\r
8527 \r
8528         case IDC_GLT_Default:\r
8529             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8530             return TRUE;\r
8531 \r
8532         case IDC_GLT_Restore:\r
8533             GLT_TagsToList( appData.gameListTags );\r
8534             return TRUE;\r
8535 \r
8536         case IDC_GLT_Up:\r
8537             GLT_MoveSelection( hDlg, -1 );\r
8538             return TRUE;\r
8539 \r
8540         case IDC_GLT_Down:\r
8541             GLT_MoveSelection( hDlg, +1 );\r
8542             return TRUE;\r
8543         }\r
8544 \r
8545         break;\r
8546     }\r
8547 \r
8548     return FALSE;\r
8549 }\r
8550 \r
8551 int GameListOptions()\r
8552 {\r
8553     int result;\r
8554     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8555 \r
8556       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8557 \r
8558     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8559 \r
8560     if( result == 0 ) {\r
8561         /* [AS] Memory leak here! */\r
8562         appData.gameListTags = strdup( lpUserGLT ); \r
8563     }\r
8564 \r
8565     return result;\r
8566 }\r
8567 \r
8568 VOID\r
8569 DisplayIcsInteractionTitle(char *str)\r
8570 {\r
8571   char consoleTitle[MSG_SIZ];\r
8572 \r
8573     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8574     SetWindowText(hwndConsole, consoleTitle);\r
8575 \r
8576     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8577       char buf[MSG_SIZ], *p = buf, *q;\r
8578         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8579       do {\r
8580         q = strchr(p, ';');\r
8581         if(q) *q++ = 0;\r
8582         if(*p) ChatPopUp(p);\r
8583       } while(p=q);\r
8584     }\r
8585 \r
8586     SetActiveWindow(hwndMain);\r
8587 }\r
8588 \r
8589 void\r
8590 DrawPosition(int fullRedraw, Board board)\r
8591 {\r
8592   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8593 }\r
8594 \r
8595 void NotifyFrontendLogin()\r
8596 {\r
8597         if (hwndConsole)\r
8598                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8599 }\r
8600 \r
8601 VOID\r
8602 ResetFrontEnd()\r
8603 {\r
8604   fromX = fromY = -1;\r
8605   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8606     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8607     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8608     dragInfo.lastpos = dragInfo.pos;\r
8609     dragInfo.start.x = dragInfo.start.y = -1;\r
8610     dragInfo.from = dragInfo.start;\r
8611     ReleaseCapture();\r
8612     DrawPosition(TRUE, NULL);\r
8613   }\r
8614   TagsPopDown();\r
8615 }\r
8616 \r
8617 \r
8618 VOID\r
8619 CommentPopUp(char *title, char *str)\r
8620 {\r
8621   HWND hwnd = GetActiveWindow();\r
8622   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8623   SAY(str);\r
8624   SetActiveWindow(hwnd);\r
8625 }\r
8626 \r
8627 VOID\r
8628 CommentPopDown(void)\r
8629 {\r
8630   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8631   if (commentDialog) {\r
8632     ShowWindow(commentDialog, SW_HIDE);\r
8633   }\r
8634   commentUp = FALSE;\r
8635 }\r
8636 \r
8637 VOID\r
8638 EditCommentPopUp(int index, char *title, char *str)\r
8639 {\r
8640   EitherCommentPopUp(index, title, str, TRUE);\r
8641 }\r
8642 \r
8643 \r
8644 VOID\r
8645 RingBell()\r
8646 {\r
8647   MyPlaySound(&sounds[(int)SoundMove]);\r
8648 }\r
8649 \r
8650 VOID PlayIcsWinSound()\r
8651 {\r
8652   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8653 }\r
8654 \r
8655 VOID PlayIcsLossSound()\r
8656 {\r
8657   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8658 }\r
8659 \r
8660 VOID PlayIcsDrawSound()\r
8661 {\r
8662   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8663 }\r
8664 \r
8665 VOID PlayIcsUnfinishedSound()\r
8666 {\r
8667   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8668 }\r
8669 \r
8670 VOID\r
8671 PlayAlarmSound()\r
8672 {\r
8673   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8674 }\r
8675 \r
8676 VOID\r
8677 PlayTellSound()\r
8678 {\r
8679   MyPlaySound(&textAttribs[ColorTell].sound);\r
8680 }\r
8681 \r
8682 \r
8683 VOID\r
8684 EchoOn()\r
8685 {\r
8686   HWND hInput;\r
8687   consoleEcho = TRUE;\r
8688   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8689   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8690   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8691 }\r
8692 \r
8693 \r
8694 VOID\r
8695 EchoOff()\r
8696 {\r
8697   CHARFORMAT cf;\r
8698   HWND hInput;\r
8699   consoleEcho = FALSE;\r
8700   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8701   /* This works OK: set text and background both to the same color */\r
8702   cf = consoleCF;\r
8703   cf.crTextColor = COLOR_ECHOOFF;\r
8704   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8705   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8706 }\r
8707 \r
8708 /* No Raw()...? */\r
8709 \r
8710 void Colorize(ColorClass cc, int continuation)\r
8711 {\r
8712   currentColorClass = cc;\r
8713   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8714   consoleCF.crTextColor = textAttribs[cc].color;\r
8715   consoleCF.dwEffects = textAttribs[cc].effects;\r
8716   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8717 }\r
8718 \r
8719 char *\r
8720 UserName()\r
8721 {\r
8722   static char buf[MSG_SIZ];\r
8723   DWORD bufsiz = MSG_SIZ;\r
8724 \r
8725   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8726         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8727   }\r
8728   if (!GetUserName(buf, &bufsiz)) {\r
8729     /*DisplayError("Error getting user name", GetLastError());*/\r
8730     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8731   }\r
8732   return buf;\r
8733 }\r
8734 \r
8735 char *\r
8736 HostName()\r
8737 {\r
8738   static char buf[MSG_SIZ];\r
8739   DWORD bufsiz = MSG_SIZ;\r
8740 \r
8741   if (!GetComputerName(buf, &bufsiz)) {\r
8742     /*DisplayError("Error getting host name", GetLastError());*/\r
8743     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8744   }\r
8745   return buf;\r
8746 }\r
8747 \r
8748 \r
8749 int\r
8750 ClockTimerRunning()\r
8751 {\r
8752   return clockTimerEvent != 0;\r
8753 }\r
8754 \r
8755 int\r
8756 StopClockTimer()\r
8757 {\r
8758   if (clockTimerEvent == 0) return FALSE;\r
8759   KillTimer(hwndMain, clockTimerEvent);\r
8760   clockTimerEvent = 0;\r
8761   return TRUE;\r
8762 }\r
8763 \r
8764 void\r
8765 StartClockTimer(long millisec)\r
8766 {\r
8767   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8768                              (UINT) millisec, NULL);\r
8769 }\r
8770 \r
8771 void\r
8772 DisplayWhiteClock(long timeRemaining, int highlight)\r
8773 {\r
8774   HDC hdc;\r
8775   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8776 \r
8777   if(appData.noGUI) return;\r
8778   hdc = GetDC(hwndMain);\r
8779   if (!IsIconic(hwndMain)) {\r
8780     DisplayAClock(hdc, timeRemaining, highlight, \r
8781                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8782   }\r
8783   if (highlight && iconCurrent == iconBlack) {\r
8784     iconCurrent = iconWhite;\r
8785     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8786     if (IsIconic(hwndMain)) {\r
8787       DrawIcon(hdc, 2, 2, iconCurrent);\r
8788     }\r
8789   }\r
8790   (void) ReleaseDC(hwndMain, hdc);\r
8791   if (hwndConsole)\r
8792     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8793 }\r
8794 \r
8795 void\r
8796 DisplayBlackClock(long timeRemaining, int highlight)\r
8797 {\r
8798   HDC hdc;\r
8799   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8800 \r
8801   if(appData.noGUI) return;\r
8802   hdc = GetDC(hwndMain);\r
8803   if (!IsIconic(hwndMain)) {\r
8804     DisplayAClock(hdc, timeRemaining, highlight, \r
8805                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8806   }\r
8807   if (highlight && iconCurrent == iconWhite) {\r
8808     iconCurrent = iconBlack;\r
8809     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8810     if (IsIconic(hwndMain)) {\r
8811       DrawIcon(hdc, 2, 2, iconCurrent);\r
8812     }\r
8813   }\r
8814   (void) ReleaseDC(hwndMain, hdc);\r
8815   if (hwndConsole)\r
8816     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8817 }\r
8818 \r
8819 \r
8820 int\r
8821 LoadGameTimerRunning()\r
8822 {\r
8823   return loadGameTimerEvent != 0;\r
8824 }\r
8825 \r
8826 int\r
8827 StopLoadGameTimer()\r
8828 {\r
8829   if (loadGameTimerEvent == 0) return FALSE;\r
8830   KillTimer(hwndMain, loadGameTimerEvent);\r
8831   loadGameTimerEvent = 0;\r
8832   return TRUE;\r
8833 }\r
8834 \r
8835 void\r
8836 StartLoadGameTimer(long millisec)\r
8837 {\r
8838   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8839                                 (UINT) millisec, NULL);\r
8840 }\r
8841 \r
8842 void\r
8843 AutoSaveGame()\r
8844 {\r
8845   char *defName;\r
8846   FILE *f;\r
8847   char fileTitle[MSG_SIZ];\r
8848 \r
8849   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8850   f = OpenFileDialog(hwndMain, "a", defName,\r
8851                      appData.oldSaveStyle ? "gam" : "pgn",\r
8852                      GAME_FILT, \r
8853                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8854   if (f != NULL) {\r
8855     SaveGame(f, 0, "");\r
8856     fclose(f);\r
8857   }\r
8858 }\r
8859 \r
8860 \r
8861 void\r
8862 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8863 {\r
8864   if (delayedTimerEvent != 0) {\r
8865     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8866       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8867     }\r
8868     KillTimer(hwndMain, delayedTimerEvent);\r
8869     delayedTimerEvent = 0;\r
8870     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8871     delayedTimerCallback();\r
8872   }\r
8873   delayedTimerCallback = cb;\r
8874   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8875                                 (UINT) millisec, NULL);\r
8876 }\r
8877 \r
8878 DelayedEventCallback\r
8879 GetDelayedEvent()\r
8880 {\r
8881   if (delayedTimerEvent) {\r
8882     return delayedTimerCallback;\r
8883   } else {\r
8884     return NULL;\r
8885   }\r
8886 }\r
8887 \r
8888 void\r
8889 CancelDelayedEvent()\r
8890 {\r
8891   if (delayedTimerEvent) {\r
8892     KillTimer(hwndMain, delayedTimerEvent);\r
8893     delayedTimerEvent = 0;\r
8894   }\r
8895 }\r
8896 \r
8897 DWORD GetWin32Priority(int nice)\r
8898 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8899 /*\r
8900 REALTIME_PRIORITY_CLASS     0x00000100\r
8901 HIGH_PRIORITY_CLASS         0x00000080\r
8902 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8903 NORMAL_PRIORITY_CLASS       0x00000020\r
8904 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8905 IDLE_PRIORITY_CLASS         0x00000040\r
8906 */\r
8907         if (nice < -15) return 0x00000080;\r
8908         if (nice < 0)   return 0x00008000;\r
8909         if (nice == 0)  return 0x00000020;\r
8910         if (nice < 15)  return 0x00004000;\r
8911         return 0x00000040;\r
8912 }\r
8913 \r
8914 void RunCommand(char *cmdLine)\r
8915 {\r
8916   /* Now create the child process. */\r
8917   STARTUPINFO siStartInfo;\r
8918   PROCESS_INFORMATION piProcInfo;\r
8919 \r
8920   siStartInfo.cb = sizeof(STARTUPINFO);\r
8921   siStartInfo.lpReserved = NULL;\r
8922   siStartInfo.lpDesktop = NULL;\r
8923   siStartInfo.lpTitle = NULL;\r
8924   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8925   siStartInfo.cbReserved2 = 0;\r
8926   siStartInfo.lpReserved2 = NULL;\r
8927   siStartInfo.hStdInput = NULL;\r
8928   siStartInfo.hStdOutput = NULL;\r
8929   siStartInfo.hStdError = NULL;\r
8930 \r
8931   CreateProcess(NULL,\r
8932                 cmdLine,           /* command line */\r
8933                 NULL,      /* process security attributes */\r
8934                 NULL,      /* primary thread security attrs */\r
8935                 TRUE,      /* handles are inherited */\r
8936                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8937                 NULL,      /* use parent's environment */\r
8938                 NULL,\r
8939                 &siStartInfo, /* STARTUPINFO pointer */\r
8940                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8941 \r
8942   CloseHandle(piProcInfo.hThread);\r
8943 }\r
8944 \r
8945 /* Start a child process running the given program.\r
8946    The process's standard output can be read from "from", and its\r
8947    standard input can be written to "to".\r
8948    Exit with fatal error if anything goes wrong.\r
8949    Returns an opaque pointer that can be used to destroy the process\r
8950    later.\r
8951 */\r
8952 int\r
8953 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8954 {\r
8955 #define BUFSIZE 4096\r
8956 \r
8957   HANDLE hChildStdinRd, hChildStdinWr,\r
8958     hChildStdoutRd, hChildStdoutWr;\r
8959   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8960   SECURITY_ATTRIBUTES saAttr;\r
8961   BOOL fSuccess;\r
8962   PROCESS_INFORMATION piProcInfo;\r
8963   STARTUPINFO siStartInfo;\r
8964   ChildProc *cp;\r
8965   char buf[MSG_SIZ];\r
8966   DWORD err;\r
8967 \r
8968   if (appData.debugMode) {\r
8969     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8970   }\r
8971 \r
8972   *pr = NoProc;\r
8973 \r
8974   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8975   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8976   saAttr.bInheritHandle = TRUE;\r
8977   saAttr.lpSecurityDescriptor = NULL;\r
8978 \r
8979   /*\r
8980    * The steps for redirecting child's STDOUT:\r
8981    *     1. Create anonymous pipe to be STDOUT for child.\r
8982    *     2. Create a noninheritable duplicate of read handle,\r
8983    *         and close the inheritable read handle.\r
8984    */\r
8985 \r
8986   /* Create a pipe for the child's STDOUT. */\r
8987   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8988     return GetLastError();\r
8989   }\r
8990 \r
8991   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8992   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8993                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8994                              FALSE,     /* not inherited */\r
8995                              DUPLICATE_SAME_ACCESS);\r
8996   if (! fSuccess) {\r
8997     return GetLastError();\r
8998   }\r
8999   CloseHandle(hChildStdoutRd);\r
9000 \r
9001   /*\r
9002    * The steps for redirecting child's STDIN:\r
9003    *     1. Create anonymous pipe to be STDIN for child.\r
9004    *     2. Create a noninheritable duplicate of write handle,\r
9005    *         and close the inheritable write handle.\r
9006    */\r
9007 \r
9008   /* Create a pipe for the child's STDIN. */\r
9009   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9010     return GetLastError();\r
9011   }\r
9012 \r
9013   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9014   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9015                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9016                              FALSE,     /* not inherited */\r
9017                              DUPLICATE_SAME_ACCESS);\r
9018   if (! fSuccess) {\r
9019     return GetLastError();\r
9020   }\r
9021   CloseHandle(hChildStdinWr);\r
9022 \r
9023   /* Arrange to (1) look in dir for the child .exe file, and\r
9024    * (2) have dir be the child's working directory.  Interpret\r
9025    * dir relative to the directory WinBoard loaded from. */\r
9026   GetCurrentDirectory(MSG_SIZ, buf);\r
9027   SetCurrentDirectory(installDir);\r
9028   SetCurrentDirectory(dir);\r
9029 \r
9030   /* Now create the child process. */\r
9031 \r
9032   siStartInfo.cb = sizeof(STARTUPINFO);\r
9033   siStartInfo.lpReserved = NULL;\r
9034   siStartInfo.lpDesktop = NULL;\r
9035   siStartInfo.lpTitle = NULL;\r
9036   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9037   siStartInfo.cbReserved2 = 0;\r
9038   siStartInfo.lpReserved2 = NULL;\r
9039   siStartInfo.hStdInput = hChildStdinRd;\r
9040   siStartInfo.hStdOutput = hChildStdoutWr;\r
9041   siStartInfo.hStdError = hChildStdoutWr;\r
9042 \r
9043   fSuccess = CreateProcess(NULL,\r
9044                            cmdLine,        /* command line */\r
9045                            NULL,           /* process security attributes */\r
9046                            NULL,           /* primary thread security attrs */\r
9047                            TRUE,           /* handles are inherited */\r
9048                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9049                            NULL,           /* use parent's environment */\r
9050                            NULL,\r
9051                            &siStartInfo, /* STARTUPINFO pointer */\r
9052                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9053 \r
9054   err = GetLastError();\r
9055   SetCurrentDirectory(buf); /* return to prev directory */\r
9056   if (! fSuccess) {\r
9057     return err;\r
9058   }\r
9059 \r
9060   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9061     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9062     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9063   }\r
9064 \r
9065   /* Close the handles we don't need in the parent */\r
9066   CloseHandle(piProcInfo.hThread);\r
9067   CloseHandle(hChildStdinRd);\r
9068   CloseHandle(hChildStdoutWr);\r
9069 \r
9070   /* Prepare return value */\r
9071   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9072   cp->kind = CPReal;\r
9073   cp->hProcess = piProcInfo.hProcess;\r
9074   cp->pid = piProcInfo.dwProcessId;\r
9075   cp->hFrom = hChildStdoutRdDup;\r
9076   cp->hTo = hChildStdinWrDup;\r
9077 \r
9078   *pr = (void *) cp;\r
9079 \r
9080   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9081      2000 where engines sometimes don't see the initial command(s)\r
9082      from WinBoard and hang.  I don't understand how that can happen,\r
9083      but the Sleep is harmless, so I've put it in.  Others have also\r
9084      reported what may be the same problem, so hopefully this will fix\r
9085      it for them too.  */\r
9086   Sleep(500);\r
9087 \r
9088   return NO_ERROR;\r
9089 }\r
9090 \r
9091 \r
9092 void\r
9093 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9094 {\r
9095   ChildProc *cp; int result;\r
9096 \r
9097   cp = (ChildProc *) pr;\r
9098   if (cp == NULL) return;\r
9099 \r
9100   switch (cp->kind) {\r
9101   case CPReal:\r
9102     /* TerminateProcess is considered harmful, so... */\r
9103     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9104     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9105     /* The following doesn't work because the chess program\r
9106        doesn't "have the same console" as WinBoard.  Maybe\r
9107        we could arrange for this even though neither WinBoard\r
9108        nor the chess program uses a console for stdio? */\r
9109     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9110 \r
9111     /* [AS] Special termination modes for misbehaving programs... */\r
9112     if( signal == 9 ) { \r
9113         result = TerminateProcess( cp->hProcess, 0 );\r
9114 \r
9115         if ( appData.debugMode) {\r
9116             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9117         }\r
9118     }\r
9119     else if( signal == 10 ) {\r
9120         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9121 \r
9122         if( dw != WAIT_OBJECT_0 ) {\r
9123             result = TerminateProcess( cp->hProcess, 0 );\r
9124 \r
9125             if ( appData.debugMode) {\r
9126                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9127             }\r
9128 \r
9129         }\r
9130     }\r
9131 \r
9132     CloseHandle(cp->hProcess);\r
9133     break;\r
9134 \r
9135   case CPComm:\r
9136     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9137     break;\r
9138 \r
9139   case CPSock:\r
9140     closesocket(cp->sock);\r
9141     WSACleanup();\r
9142     break;\r
9143 \r
9144   case CPRcmd:\r
9145     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9146     closesocket(cp->sock);\r
9147     closesocket(cp->sock2);\r
9148     WSACleanup();\r
9149     break;\r
9150   }\r
9151   free(cp);\r
9152 }\r
9153 \r
9154 void\r
9155 InterruptChildProcess(ProcRef pr)\r
9156 {\r
9157   ChildProc *cp;\r
9158 \r
9159   cp = (ChildProc *) pr;\r
9160   if (cp == NULL) return;\r
9161   switch (cp->kind) {\r
9162   case CPReal:\r
9163     /* The following doesn't work because the chess program\r
9164        doesn't "have the same console" as WinBoard.  Maybe\r
9165        we could arrange for this even though neither WinBoard\r
9166        nor the chess program uses a console for stdio */\r
9167     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9168     break;\r
9169 \r
9170   case CPComm:\r
9171   case CPSock:\r
9172     /* Can't interrupt */\r
9173     break;\r
9174 \r
9175   case CPRcmd:\r
9176     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9177     break;\r
9178   }\r
9179 }\r
9180 \r
9181 \r
9182 int\r
9183 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9184 {\r
9185   char cmdLine[MSG_SIZ];\r
9186 \r
9187   if (port[0] == NULLCHAR) {\r
9188     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9189   } else {\r
9190     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9191   }\r
9192   return StartChildProcess(cmdLine, "", pr);\r
9193 }\r
9194 \r
9195 \r
9196 /* Code to open TCP sockets */\r
9197 \r
9198 int\r
9199 OpenTCP(char *host, char *port, ProcRef *pr)\r
9200 {\r
9201   ChildProc *cp;\r
9202   int err;\r
9203   SOCKET s;\r
9204   struct sockaddr_in sa, mysa;\r
9205   struct hostent FAR *hp;\r
9206   unsigned short uport;\r
9207   WORD wVersionRequested;\r
9208   WSADATA wsaData;\r
9209 \r
9210   /* Initialize socket DLL */\r
9211   wVersionRequested = MAKEWORD(1, 1);\r
9212   err = WSAStartup(wVersionRequested, &wsaData);\r
9213   if (err != 0) return err;\r
9214 \r
9215   /* Make socket */\r
9216   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9217     err = WSAGetLastError();\r
9218     WSACleanup();\r
9219     return err;\r
9220   }\r
9221 \r
9222   /* Bind local address using (mostly) don't-care values.\r
9223    */\r
9224   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9225   mysa.sin_family = AF_INET;\r
9226   mysa.sin_addr.s_addr = INADDR_ANY;\r
9227   uport = (unsigned short) 0;\r
9228   mysa.sin_port = htons(uport);\r
9229   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9230       == SOCKET_ERROR) {\r
9231     err = WSAGetLastError();\r
9232     WSACleanup();\r
9233     return err;\r
9234   }\r
9235 \r
9236   /* Resolve remote host name */\r
9237   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9238   if (!(hp = gethostbyname(host))) {\r
9239     unsigned int b0, b1, b2, b3;\r
9240 \r
9241     err = WSAGetLastError();\r
9242 \r
9243     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9244       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9245       hp->h_addrtype = AF_INET;\r
9246       hp->h_length = 4;\r
9247       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9248       hp->h_addr_list[0] = (char *) malloc(4);\r
9249       hp->h_addr_list[0][0] = (char) b0;\r
9250       hp->h_addr_list[0][1] = (char) b1;\r
9251       hp->h_addr_list[0][2] = (char) b2;\r
9252       hp->h_addr_list[0][3] = (char) b3;\r
9253     } else {\r
9254       WSACleanup();\r
9255       return err;\r
9256     }\r
9257   }\r
9258   sa.sin_family = hp->h_addrtype;\r
9259   uport = (unsigned short) atoi(port);\r
9260   sa.sin_port = htons(uport);\r
9261   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9262 \r
9263   /* Make connection */\r
9264   if (connect(s, (struct sockaddr *) &sa,\r
9265               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9266     err = WSAGetLastError();\r
9267     WSACleanup();\r
9268     return err;\r
9269   }\r
9270 \r
9271   /* Prepare return value */\r
9272   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9273   cp->kind = CPSock;\r
9274   cp->sock = s;\r
9275   *pr = (ProcRef *) cp;\r
9276 \r
9277   return NO_ERROR;\r
9278 }\r
9279 \r
9280 int\r
9281 OpenCommPort(char *name, ProcRef *pr)\r
9282 {\r
9283   HANDLE h;\r
9284   COMMTIMEOUTS ct;\r
9285   ChildProc *cp;\r
9286   char fullname[MSG_SIZ];\r
9287 \r
9288   if (*name != '\\')\r
9289     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9290   else\r
9291     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9292 \r
9293   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9294                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9295   if (h == (HANDLE) -1) {\r
9296     return GetLastError();\r
9297   }\r
9298   hCommPort = h;\r
9299 \r
9300   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9301 \r
9302   /* Accumulate characters until a 100ms pause, then parse */\r
9303   ct.ReadIntervalTimeout = 100;\r
9304   ct.ReadTotalTimeoutMultiplier = 0;\r
9305   ct.ReadTotalTimeoutConstant = 0;\r
9306   ct.WriteTotalTimeoutMultiplier = 0;\r
9307   ct.WriteTotalTimeoutConstant = 0;\r
9308   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9309 \r
9310   /* Prepare return value */\r
9311   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9312   cp->kind = CPComm;\r
9313   cp->hFrom = h;\r
9314   cp->hTo = h;\r
9315   *pr = (ProcRef *) cp;\r
9316 \r
9317   return NO_ERROR;\r
9318 }\r
9319 \r
9320 int\r
9321 OpenLoopback(ProcRef *pr)\r
9322 {\r
9323   DisplayFatalError(_("Not implemented"), 0, 1);\r
9324   return NO_ERROR;\r
9325 }\r
9326 \r
9327 \r
9328 int\r
9329 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9330 {\r
9331   ChildProc *cp;\r
9332   int err;\r
9333   SOCKET s, s2, s3;\r
9334   struct sockaddr_in sa, mysa;\r
9335   struct hostent FAR *hp;\r
9336   unsigned short uport;\r
9337   WORD wVersionRequested;\r
9338   WSADATA wsaData;\r
9339   int fromPort;\r
9340   char stderrPortStr[MSG_SIZ];\r
9341 \r
9342   /* Initialize socket DLL */\r
9343   wVersionRequested = MAKEWORD(1, 1);\r
9344   err = WSAStartup(wVersionRequested, &wsaData);\r
9345   if (err != 0) return err;\r
9346 \r
9347   /* Resolve remote host name */\r
9348   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9349   if (!(hp = gethostbyname(host))) {\r
9350     unsigned int b0, b1, b2, b3;\r
9351 \r
9352     err = WSAGetLastError();\r
9353 \r
9354     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9355       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9356       hp->h_addrtype = AF_INET;\r
9357       hp->h_length = 4;\r
9358       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9359       hp->h_addr_list[0] = (char *) malloc(4);\r
9360       hp->h_addr_list[0][0] = (char) b0;\r
9361       hp->h_addr_list[0][1] = (char) b1;\r
9362       hp->h_addr_list[0][2] = (char) b2;\r
9363       hp->h_addr_list[0][3] = (char) b3;\r
9364     } else {\r
9365       WSACleanup();\r
9366       return err;\r
9367     }\r
9368   }\r
9369   sa.sin_family = hp->h_addrtype;\r
9370   uport = (unsigned short) 514;\r
9371   sa.sin_port = htons(uport);\r
9372   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9373 \r
9374   /* Bind local socket to unused "privileged" port address\r
9375    */\r
9376   s = INVALID_SOCKET;\r
9377   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9378   mysa.sin_family = AF_INET;\r
9379   mysa.sin_addr.s_addr = INADDR_ANY;\r
9380   for (fromPort = 1023;; fromPort--) {\r
9381     if (fromPort < 0) {\r
9382       WSACleanup();\r
9383       return WSAEADDRINUSE;\r
9384     }\r
9385     if (s == INVALID_SOCKET) {\r
9386       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9387         err = WSAGetLastError();\r
9388         WSACleanup();\r
9389         return err;\r
9390       }\r
9391     }\r
9392     uport = (unsigned short) fromPort;\r
9393     mysa.sin_port = htons(uport);\r
9394     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9395         == SOCKET_ERROR) {\r
9396       err = WSAGetLastError();\r
9397       if (err == WSAEADDRINUSE) continue;\r
9398       WSACleanup();\r
9399       return err;\r
9400     }\r
9401     if (connect(s, (struct sockaddr *) &sa,\r
9402       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9403       err = WSAGetLastError();\r
9404       if (err == WSAEADDRINUSE) {\r
9405         closesocket(s);\r
9406         s = -1;\r
9407         continue;\r
9408       }\r
9409       WSACleanup();\r
9410       return err;\r
9411     }\r
9412     break;\r
9413   }\r
9414 \r
9415   /* Bind stderr local socket to unused "privileged" port address\r
9416    */\r
9417   s2 = INVALID_SOCKET;\r
9418   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9419   mysa.sin_family = AF_INET;\r
9420   mysa.sin_addr.s_addr = INADDR_ANY;\r
9421   for (fromPort = 1023;; fromPort--) {\r
9422     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9423     if (fromPort < 0) {\r
9424       (void) closesocket(s);\r
9425       WSACleanup();\r
9426       return WSAEADDRINUSE;\r
9427     }\r
9428     if (s2 == INVALID_SOCKET) {\r
9429       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9430         err = WSAGetLastError();\r
9431         closesocket(s);\r
9432         WSACleanup();\r
9433         return err;\r
9434       }\r
9435     }\r
9436     uport = (unsigned short) fromPort;\r
9437     mysa.sin_port = htons(uport);\r
9438     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9439         == SOCKET_ERROR) {\r
9440       err = WSAGetLastError();\r
9441       if (err == WSAEADDRINUSE) continue;\r
9442       (void) closesocket(s);\r
9443       WSACleanup();\r
9444       return err;\r
9445     }\r
9446     if (listen(s2, 1) == SOCKET_ERROR) {\r
9447       err = WSAGetLastError();\r
9448       if (err == WSAEADDRINUSE) {\r
9449         closesocket(s2);\r
9450         s2 = INVALID_SOCKET;\r
9451         continue;\r
9452       }\r
9453       (void) closesocket(s);\r
9454       (void) closesocket(s2);\r
9455       WSACleanup();\r
9456       return err;\r
9457     }\r
9458     break;\r
9459   }\r
9460   prevStderrPort = fromPort; // remember port used\r
9461   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9462 \r
9463   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9464     err = WSAGetLastError();\r
9465     (void) closesocket(s);\r
9466     (void) closesocket(s2);\r
9467     WSACleanup();\r
9468     return err;\r
9469   }\r
9470 \r
9471   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9472     err = WSAGetLastError();\r
9473     (void) closesocket(s);\r
9474     (void) closesocket(s2);\r
9475     WSACleanup();\r
9476     return err;\r
9477   }\r
9478   if (*user == NULLCHAR) user = UserName();\r
9479   if (send(s, user, strlen(user) + 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   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9487     err = WSAGetLastError();\r
9488     (void) closesocket(s);\r
9489     (void) closesocket(s2);\r
9490     WSACleanup();\r
9491     return err;\r
9492   }\r
9493 \r
9494   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9495     err = WSAGetLastError();\r
9496     (void) closesocket(s);\r
9497     (void) closesocket(s2);\r
9498     WSACleanup();\r
9499     return err;\r
9500   }\r
9501   (void) closesocket(s2);  /* Stop listening */\r
9502 \r
9503   /* Prepare return value */\r
9504   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9505   cp->kind = CPRcmd;\r
9506   cp->sock = s;\r
9507   cp->sock2 = s3;\r
9508   *pr = (ProcRef *) cp;\r
9509 \r
9510   return NO_ERROR;\r
9511 }\r
9512 \r
9513 \r
9514 InputSourceRef\r
9515 AddInputSource(ProcRef pr, int lineByLine,\r
9516                InputCallback func, VOIDSTAR closure)\r
9517 {\r
9518   InputSource *is, *is2 = NULL;\r
9519   ChildProc *cp = (ChildProc *) pr;\r
9520 \r
9521   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9522   is->lineByLine = lineByLine;\r
9523   is->func = func;\r
9524   is->closure = closure;\r
9525   is->second = NULL;\r
9526   is->next = is->buf;\r
9527   if (pr == NoProc) {\r
9528     is->kind = CPReal;\r
9529     consoleInputSource = is;\r
9530   } else {\r
9531     is->kind = cp->kind;\r
9532     /* \r
9533         [AS] Try to avoid a race condition if the thread is given control too early:\r
9534         we create all threads suspended so that the is->hThread variable can be\r
9535         safely assigned, then let the threads start with ResumeThread.\r
9536     */\r
9537     switch (cp->kind) {\r
9538     case CPReal:\r
9539       is->hFile = cp->hFrom;\r
9540       cp->hFrom = NULL; /* now owned by InputThread */\r
9541       is->hThread =\r
9542         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9543                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9544       break;\r
9545 \r
9546     case CPComm:\r
9547       is->hFile = cp->hFrom;\r
9548       cp->hFrom = NULL; /* now owned by InputThread */\r
9549       is->hThread =\r
9550         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9551                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9552       break;\r
9553 \r
9554     case CPSock:\r
9555       is->sock = cp->sock;\r
9556       is->hThread =\r
9557         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9558                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9559       break;\r
9560 \r
9561     case CPRcmd:\r
9562       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9563       *is2 = *is;\r
9564       is->sock = cp->sock;\r
9565       is->second = is2;\r
9566       is2->sock = cp->sock2;\r
9567       is2->second = is2;\r
9568       is->hThread =\r
9569         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9570                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9571       is2->hThread =\r
9572         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9573                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9574       break;\r
9575     }\r
9576 \r
9577     if( is->hThread != NULL ) {\r
9578         ResumeThread( is->hThread );\r
9579     }\r
9580 \r
9581     if( is2 != NULL && is2->hThread != NULL ) {\r
9582         ResumeThread( is2->hThread );\r
9583     }\r
9584   }\r
9585 \r
9586   return (InputSourceRef) is;\r
9587 }\r
9588 \r
9589 void\r
9590 RemoveInputSource(InputSourceRef isr)\r
9591 {\r
9592   InputSource *is;\r
9593 \r
9594   is = (InputSource *) isr;\r
9595   is->hThread = NULL;  /* tell thread to stop */\r
9596   CloseHandle(is->hThread);\r
9597   if (is->second != NULL) {\r
9598     is->second->hThread = NULL;\r
9599     CloseHandle(is->second->hThread);\r
9600   }\r
9601 }\r
9602 \r
9603 int no_wrap(char *message, int count)\r
9604 {\r
9605     ConsoleOutput(message, count, FALSE);\r
9606     return count;\r
9607 }\r
9608 \r
9609 int\r
9610 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9611 {\r
9612   DWORD dOutCount;\r
9613   int outCount = SOCKET_ERROR;\r
9614   ChildProc *cp = (ChildProc *) pr;\r
9615   static OVERLAPPED ovl;\r
9616   static int line = 0;\r
9617 \r
9618   if (pr == NoProc)\r
9619   {\r
9620     if (appData.noJoin || !appData.useInternalWrap)\r
9621       return no_wrap(message, count);\r
9622     else\r
9623     {\r
9624       int width = get_term_width();\r
9625       int len = wrap(NULL, message, count, width, &line);\r
9626       char *msg = malloc(len);\r
9627       int dbgchk;\r
9628 \r
9629       if (!msg)\r
9630         return no_wrap(message, count);\r
9631       else\r
9632       {\r
9633         dbgchk = wrap(msg, message, count, width, &line);\r
9634         if (dbgchk != len && appData.debugMode)\r
9635             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9636         ConsoleOutput(msg, len, FALSE);\r
9637         free(msg);\r
9638         return len;\r
9639       }\r
9640     }\r
9641   }\r
9642 \r
9643   if (ovl.hEvent == NULL) {\r
9644     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9645   }\r
9646   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9647 \r
9648   switch (cp->kind) {\r
9649   case CPSock:\r
9650   case CPRcmd:\r
9651     outCount = send(cp->sock, message, count, 0);\r
9652     if (outCount == SOCKET_ERROR) {\r
9653       *outError = WSAGetLastError();\r
9654     } else {\r
9655       *outError = NO_ERROR;\r
9656     }\r
9657     break;\r
9658 \r
9659   case CPReal:\r
9660     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9661                   &dOutCount, NULL)) {\r
9662       *outError = NO_ERROR;\r
9663       outCount = (int) dOutCount;\r
9664     } else {\r
9665       *outError = GetLastError();\r
9666     }\r
9667     break;\r
9668 \r
9669   case CPComm:\r
9670     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9671                             &dOutCount, &ovl);\r
9672     if (*outError == NO_ERROR) {\r
9673       outCount = (int) dOutCount;\r
9674     }\r
9675     break;\r
9676   }\r
9677   return outCount;\r
9678 }\r
9679 \r
9680 void\r
9681 DoSleep(int n)\r
9682 {\r
9683     if(n != 0) Sleep(n);\r
9684 }\r
9685 \r
9686 int\r
9687 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9688                        long msdelay)\r
9689 {\r
9690   /* Ignore delay, not implemented for WinBoard */\r
9691   return OutputToProcess(pr, message, count, outError);\r
9692 }\r
9693 \r
9694 \r
9695 void\r
9696 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9697                         char *buf, int count, int error)\r
9698 {\r
9699   DisplayFatalError(_("Not implemented"), 0, 1);\r
9700 }\r
9701 \r
9702 /* see wgamelist.c for Game List functions */\r
9703 /* see wedittags.c for Edit Tags functions */\r
9704 \r
9705 \r
9706 VOID\r
9707 ICSInitScript()\r
9708 {\r
9709   FILE *f;\r
9710   char buf[MSG_SIZ];\r
9711   char *dummy;\r
9712 \r
9713   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9714     f = fopen(buf, "r");\r
9715     if (f != NULL) {\r
9716       ProcessICSInitScript(f);\r
9717       fclose(f);\r
9718     }\r
9719   }\r
9720 }\r
9721 \r
9722 \r
9723 VOID\r
9724 StartAnalysisClock()\r
9725 {\r
9726   if (analysisTimerEvent) return;\r
9727   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9728                                         (UINT) 2000, NULL);\r
9729 }\r
9730 \r
9731 VOID\r
9732 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9733 {\r
9734   highlightInfo.sq[0].x = fromX;\r
9735   highlightInfo.sq[0].y = fromY;\r
9736   highlightInfo.sq[1].x = toX;\r
9737   highlightInfo.sq[1].y = toY;\r
9738 }\r
9739 \r
9740 VOID\r
9741 ClearHighlights()\r
9742 {\r
9743   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9744     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9745 }\r
9746 \r
9747 VOID\r
9748 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9749 {\r
9750   premoveHighlightInfo.sq[0].x = fromX;\r
9751   premoveHighlightInfo.sq[0].y = fromY;\r
9752   premoveHighlightInfo.sq[1].x = toX;\r
9753   premoveHighlightInfo.sq[1].y = toY;\r
9754 }\r
9755 \r
9756 VOID\r
9757 ClearPremoveHighlights()\r
9758 {\r
9759   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9760     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9761 }\r
9762 \r
9763 VOID\r
9764 ShutDownFrontEnd()\r
9765 {\r
9766   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9767   DeleteClipboardTempFiles();\r
9768 }\r
9769 \r
9770 void\r
9771 BoardToTop()\r
9772 {\r
9773     if (IsIconic(hwndMain))\r
9774       ShowWindow(hwndMain, SW_RESTORE);\r
9775 \r
9776     SetActiveWindow(hwndMain);\r
9777 }\r
9778 \r
9779 /*\r
9780  * Prototypes for animation support routines\r
9781  */\r
9782 static void ScreenSquare(int column, int row, POINT * pt);\r
9783 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9784      POINT frames[], int * nFrames);\r
9785 \r
9786 \r
9787 #define kFactor 4\r
9788 \r
9789 void\r
9790 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9791 {       // [HGM] atomic: animate blast wave\r
9792         int i;\r
9793 \r
9794         explodeInfo.fromX = fromX;\r
9795         explodeInfo.fromY = fromY;\r
9796         explodeInfo.toX = toX;\r
9797         explodeInfo.toY = toY;\r
9798         for(i=1; i<4*kFactor; i++) {\r
9799             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9800             DrawPosition(FALSE, board);\r
9801             Sleep(appData.animSpeed);\r
9802         }\r
9803         explodeInfo.radius = 0;\r
9804         DrawPosition(TRUE, board);\r
9805 }\r
9806 \r
9807 void\r
9808 AnimateMove(board, fromX, fromY, toX, toY)\r
9809      Board board;\r
9810      int fromX;\r
9811      int fromY;\r
9812      int toX;\r
9813      int toY;\r
9814 {\r
9815   ChessSquare piece;\r
9816   POINT start, finish, mid;\r
9817   POINT frames[kFactor * 2 + 1];\r
9818   int nFrames, n;\r
9819 \r
9820   if (!appData.animate) return;\r
9821   if (doingSizing) return;\r
9822   if (fromY < 0 || fromX < 0) return;\r
9823   piece = board[fromY][fromX];\r
9824   if (piece >= EmptySquare) return;\r
9825 \r
9826   ScreenSquare(fromX, fromY, &start);\r
9827   ScreenSquare(toX, toY, &finish);\r
9828 \r
9829   /* All moves except knight jumps move in straight line */\r
9830   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9831     mid.x = start.x + (finish.x - start.x) / 2;\r
9832     mid.y = start.y + (finish.y - start.y) / 2;\r
9833   } else {\r
9834     /* Knight: make straight movement then diagonal */\r
9835     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9836        mid.x = start.x + (finish.x - start.x) / 2;\r
9837        mid.y = start.y;\r
9838      } else {\r
9839        mid.x = start.x;\r
9840        mid.y = start.y + (finish.y - start.y) / 2;\r
9841      }\r
9842   }\r
9843   \r
9844   /* Don't use as many frames for very short moves */\r
9845   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9846     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9847   else\r
9848     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9849 \r
9850   animInfo.from.x = fromX;\r
9851   animInfo.from.y = fromY;\r
9852   animInfo.to.x = toX;\r
9853   animInfo.to.y = toY;\r
9854   animInfo.lastpos = start;\r
9855   animInfo.piece = piece;\r
9856   for (n = 0; n < nFrames; n++) {\r
9857     animInfo.pos = frames[n];\r
9858     DrawPosition(FALSE, NULL);\r
9859     animInfo.lastpos = animInfo.pos;\r
9860     Sleep(appData.animSpeed);\r
9861   }\r
9862   animInfo.pos = finish;\r
9863   DrawPosition(FALSE, NULL);\r
9864   animInfo.piece = EmptySquare;\r
9865   Explode(board, fromX, fromY, toX, toY);\r
9866 }\r
9867 \r
9868 /*      Convert board position to corner of screen rect and color       */\r
9869 \r
9870 static void\r
9871 ScreenSquare(column, row, pt)\r
9872      int column; int row; POINT * pt;\r
9873 {\r
9874   if (flipView) {\r
9875     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9876     pt->y = lineGap + row * (squareSize + lineGap);\r
9877   } else {\r
9878     pt->x = lineGap + column * (squareSize + lineGap);\r
9879     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9880   }\r
9881 }\r
9882 \r
9883 /*      Generate a series of frame coords from start->mid->finish.\r
9884         The movement rate doubles until the half way point is\r
9885         reached, then halves back down to the final destination,\r
9886         which gives a nice slow in/out effect. The algorithmn\r
9887         may seem to generate too many intermediates for short\r
9888         moves, but remember that the purpose is to attract the\r
9889         viewers attention to the piece about to be moved and\r
9890         then to where it ends up. Too few frames would be less\r
9891         noticeable.                                             */\r
9892 \r
9893 static void\r
9894 Tween(start, mid, finish, factor, frames, nFrames)\r
9895      POINT * start; POINT * mid;\r
9896      POINT * finish; int factor;\r
9897      POINT frames[]; int * nFrames;\r
9898 {\r
9899   int n, fraction = 1, count = 0;\r
9900 \r
9901   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9902   for (n = 0; n < factor; n++)\r
9903     fraction *= 2;\r
9904   for (n = 0; n < factor; n++) {\r
9905     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9906     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9907     count ++;\r
9908     fraction = fraction / 2;\r
9909   }\r
9910   \r
9911   /* Midpoint */\r
9912   frames[count] = *mid;\r
9913   count ++;\r
9914   \r
9915   /* Slow out, stepping 1/2, then 1/4, ... */\r
9916   fraction = 2;\r
9917   for (n = 0; n < factor; n++) {\r
9918     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9919     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9920     count ++;\r
9921     fraction = fraction * 2;\r
9922   }\r
9923   *nFrames = count;\r
9924 }\r
9925 \r
9926 void\r
9927 SettingsPopUp(ChessProgramState *cps)\r
9928 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9929       EngineOptionsPopup(savedHwnd, cps);\r
9930 }\r
9931 \r
9932 int flock(int fid, int code)\r
9933 {\r
9934     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9935     OVERLAPPED ov;\r
9936     ov.hEvent = NULL;\r
9937     ov.Offset = 0;\r
9938     ov.OffsetHigh = 0;\r
9939     switch(code) {\r
9940       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9941       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9942       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9943       default: return -1;\r
9944     }\r
9945     return 0;\r
9946 }\r