Solve WinBoard name clashes, fix zippy-analyze menu graying
[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 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
557   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
558   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
559   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
560   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
561   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1010         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1011       } else\r
1012       if(appData.directory[n] && appData.directory[n][0]) {\r
1013         SetCurrentDirectory(appData.directory[n]);\r
1014         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1015       }\r
1016   }\r
1017   SetCurrentDirectory(dir); /* return to prev directory */\r
1018 }\r
1019 \r
1020 VOID\r
1021 InitTextures()\r
1022 {\r
1023   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1024   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1025   \r
1026   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1027       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1028       liteBackTextureMode = appData.liteBackTextureMode;\r
1029 \r
1030       if (liteBackTexture == NULL && appData.debugMode) {\r
1031           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1032       }\r
1033   }\r
1034   \r
1035   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1036       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       darkBackTextureMode = appData.darkBackTextureMode;\r
1038 \r
1039       if (darkBackTexture == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1041       }\r
1042   }\r
1043 }\r
1044 \r
1045 BOOL\r
1046 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1047 {\r
1048   HWND hwnd; /* Main window handle. */\r
1049   int ibs;\r
1050   WINDOWPLACEMENT wp;\r
1051   char *filepart;\r
1052 \r
1053   hInst = hInstance;    /* Store instance handle in our global variable */\r
1054   programName = szAppName;\r
1055 \r
1056   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1057     *filepart = NULLCHAR;\r
1058   } else {\r
1059     GetCurrentDirectory(MSG_SIZ, installDir);\r
1060   }\r
1061   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1062   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1063   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1064   /* xboard, and older WinBoards, controlled the move sound with the\r
1065      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1066      always turn the option on (so that the backend will call us),\r
1067      then let the user turn the sound off by setting it to silence if\r
1068      desired.  To accommodate old winboard.ini files saved by old\r
1069      versions of WinBoard, we also turn off the sound if the option\r
1070      was initially set to false. [HGM] taken out of InitAppData */\r
1071   if (!appData.ringBellAfterMoves) {\r
1072     sounds[(int)SoundMove].name = strdup("");\r
1073     appData.ringBellAfterMoves = TRUE;\r
1074   }\r
1075   if (appData.debugMode) {\r
1076     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1077     setbuf(debugFP, NULL);\r
1078   }\r
1079 \r
1080   LoadLanguageFile(appData.language);\r
1081 \r
1082   InitBackEnd1();\r
1083 \r
1084 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1085 //  InitEngineUCI( installDir, &second );\r
1086 \r
1087   /* Create a main window for this application instance. */\r
1088   hwnd = CreateWindow(szAppName, szTitle,\r
1089                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1090                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1091                       NULL, NULL, hInstance, NULL);\r
1092   hwndMain = hwnd;\r
1093 \r
1094   /* If window could not be created, return "failure" */\r
1095   if (!hwnd) {\r
1096     return (FALSE);\r
1097   }\r
1098 \r
1099   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1100   LoadLogo(&first, 0, FALSE);\r
1101   LoadLogo(&second, 1, appData.icsActive);\r
1102 \r
1103   SetUserLogo();\r
1104 \r
1105   iconWhite = LoadIcon(hInstance, "icon_white");\r
1106   iconBlack = LoadIcon(hInstance, "icon_black");\r
1107   iconCurrent = iconWhite;\r
1108   InitDrawingColors();\r
1109   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1110   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1111   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1112     /* Compute window size for each board size, and use the largest\r
1113        size that fits on this screen as the default. */\r
1114     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1115     if (boardSize == (BoardSize)-1 &&\r
1116         winH <= screenHeight\r
1117            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1118         && winW <= screenWidth) {\r
1119       boardSize = (BoardSize)ibs;\r
1120     }\r
1121   }\r
1122 \r
1123   InitDrawingSizes(boardSize, 0);\r
1124   RecentEngineMenu(appData.recentEngineList);\r
1125   InitMenuChecks();\r
1126   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1127 \r
1128   /* [AS] Load textures if specified */\r
1129   InitTextures();\r
1130 \r
1131   mysrandom( (unsigned) time(NULL) );\r
1132 \r
1133   /* [AS] Restore layout */\r
1134   if( wpMoveHistory.visible ) {\r
1135       MoveHistoryPopUp();\r
1136   }\r
1137 \r
1138   if( wpEvalGraph.visible ) {\r
1139       EvalGraphPopUp();\r
1140   }\r
1141 \r
1142   if( wpEngineOutput.visible ) {\r
1143       EngineOutputPopUp();\r
1144   }\r
1145 \r
1146   /* Make the window visible; update its client area; and return "success" */\r
1147   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1148   wp.length = sizeof(WINDOWPLACEMENT);\r
1149   wp.flags = 0;\r
1150   wp.showCmd = nCmdShow;\r
1151   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1152   wp.rcNormalPosition.left = wpMain.x;\r
1153   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1154   wp.rcNormalPosition.top = wpMain.y;\r
1155   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1156   SetWindowPlacement(hwndMain, &wp);\r
1157 \r
1158   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1159 \r
1160   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1161                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1162 \r
1163   if (hwndConsole) {\r
1164 #if AOT_CONSOLE\r
1165     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1166                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1167 #endif\r
1168     ShowWindow(hwndConsole, nCmdShow);\r
1169     SetActiveWindow(hwndConsole);\r
1170   }\r
1171   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1172   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1173 \r
1174   return TRUE;\r
1175 \r
1176 }\r
1177 \r
1178 VOID\r
1179 InitMenuChecks()\r
1180 {\r
1181   HMENU hmenu = GetMenu(hwndMain);\r
1182 \r
1183   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1184                         MF_BYCOMMAND|((appData.icsActive &&\r
1185                                        *appData.icsCommPort != NULLCHAR) ?\r
1186                                       MF_ENABLED : MF_GRAYED));\r
1187   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1188                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1189                                      MF_CHECKED : MF_UNCHECKED));\r
1190 }\r
1191 \r
1192 //---------------------------------------------------------------------------------------------------------\r
1193 \r
1194 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1195 #define XBOARD FALSE\r
1196 \r
1197 #define OPTCHAR "/"\r
1198 #define SEPCHAR "="\r
1199 #define TOPLEVEL 0\r
1200 \r
1201 #include "args.h"\r
1202 \r
1203 // front-end part of option handling\r
1204 \r
1205 VOID\r
1206 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1207 {\r
1208   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1209   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1210   DeleteDC(hdc);\r
1211   lf->lfWidth = 0;\r
1212   lf->lfEscapement = 0;\r
1213   lf->lfOrientation = 0;\r
1214   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1215   lf->lfItalic = mfp->italic;\r
1216   lf->lfUnderline = mfp->underline;\r
1217   lf->lfStrikeOut = mfp->strikeout;\r
1218   lf->lfCharSet = mfp->charset;\r
1219   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1220   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1221   lf->lfQuality = DEFAULT_QUALITY;\r
1222   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1223     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1224 }\r
1225 \r
1226 void\r
1227 CreateFontInMF(MyFont *mf)\r
1228\r
1229   LFfromMFP(&mf->lf, &mf->mfp);\r
1230   if (mf->hf) DeleteObject(mf->hf);\r
1231   mf->hf = CreateFontIndirect(&mf->lf);\r
1232 }\r
1233 \r
1234 // [HGM] This platform-dependent table provides the location for storing the color info\r
1235 void *\r
1236 colorVariable[] = {\r
1237   &whitePieceColor, \r
1238   &blackPieceColor, \r
1239   &lightSquareColor,\r
1240   &darkSquareColor, \r
1241   &highlightSquareColor,\r
1242   &premoveHighlightColor,\r
1243   NULL,\r
1244   &consoleBackgroundColor,\r
1245   &appData.fontForeColorWhite,\r
1246   &appData.fontBackColorWhite,\r
1247   &appData.fontForeColorBlack,\r
1248   &appData.fontBackColorBlack,\r
1249   &appData.evalHistColorWhite,\r
1250   &appData.evalHistColorBlack,\r
1251   &appData.highlightArrowColor,\r
1252 };\r
1253 \r
1254 /* Command line font name parser.  NULL name means do nothing.\r
1255    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1256    For backward compatibility, syntax without the colon is also\r
1257    accepted, but font names with digits in them won't work in that case.\r
1258 */\r
1259 VOID\r
1260 ParseFontName(char *name, MyFontParams *mfp)\r
1261 {\r
1262   char *p, *q;\r
1263   if (name == NULL) return;\r
1264   p = name;\r
1265   q = strchr(p, ':');\r
1266   if (q) {\r
1267     if (q - p >= sizeof(mfp->faceName))\r
1268       ExitArgError(_("Font name too long:"), name, TRUE);\r
1269     memcpy(mfp->faceName, p, q - p);\r
1270     mfp->faceName[q - p] = NULLCHAR;\r
1271     p = q + 1;\r
1272   } else {\r
1273     q = mfp->faceName;\r
1274     while (*p && !isdigit(*p)) {\r
1275       *q++ = *p++;\r
1276       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1277         ExitArgError(_("Font name too long:"), name, TRUE);\r
1278     }\r
1279     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1280     *q = NULLCHAR;\r
1281   }\r
1282   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1283   mfp->pointSize = (float) atof(p);\r
1284   mfp->bold = (strchr(p, 'b') != NULL);\r
1285   mfp->italic = (strchr(p, 'i') != NULL);\r
1286   mfp->underline = (strchr(p, 'u') != NULL);\r
1287   mfp->strikeout = (strchr(p, 's') != NULL);\r
1288   mfp->charset = DEFAULT_CHARSET;\r
1289   q = strchr(p, 'c');\r
1290   if (q)\r
1291     mfp->charset = (BYTE) atoi(q+1);\r
1292 }\r
1293 \r
1294 void\r
1295 ParseFont(char *name, int number)\r
1296 { // wrapper to shield back-end from 'font'\r
1297   ParseFontName(name, &font[boardSize][number]->mfp);\r
1298 }\r
1299 \r
1300 void\r
1301 SetFontDefaults()\r
1302 { // in WB  we have a 2D array of fonts; this initializes their description\r
1303   int i, j;\r
1304   /* Point font array elements to structures and\r
1305      parse default font names */\r
1306   for (i=0; i<NUM_FONTS; i++) {\r
1307     for (j=0; j<NUM_SIZES; j++) {\r
1308       font[j][i] = &fontRec[j][i];\r
1309       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1310     }\r
1311   }\r
1312 }\r
1313 \r
1314 void\r
1315 CreateFonts()\r
1316 { // here we create the actual fonts from the selected descriptions\r
1317   int i, j;\r
1318   for (i=0; i<NUM_FONTS; i++) {\r
1319     for (j=0; j<NUM_SIZES; j++) {\r
1320       CreateFontInMF(font[j][i]);\r
1321     }\r
1322   }\r
1323 }\r
1324 /* Color name parser.\r
1325    X version accepts X color names, but this one\r
1326    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1327 COLORREF\r
1328 ParseColorName(char *name)\r
1329 {\r
1330   int red, green, blue, count;\r
1331   char buf[MSG_SIZ];\r
1332 \r
1333   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1334   if (count != 3) {\r
1335     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1336       &red, &green, &blue);\r
1337   }\r
1338   if (count != 3) {\r
1339     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1340     DisplayError(buf, 0);\r
1341     return RGB(0, 0, 0);\r
1342   }\r
1343   return PALETTERGB(red, green, blue);\r
1344 }\r
1345 \r
1346 void\r
1347 ParseColor(int n, char *name)\r
1348 { // for WinBoard the color is an int, which needs to be derived from the string\r
1349   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1354 {\r
1355   char *e = argValue;\r
1356   int eff = 0;\r
1357 \r
1358   while (*e) {\r
1359     if (*e == 'b')      eff |= CFE_BOLD;\r
1360     else if (*e == 'i') eff |= CFE_ITALIC;\r
1361     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1362     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1363     else if (*e == '#' || isdigit(*e)) break;\r
1364     e++;\r
1365   }\r
1366   *effects = eff;\r
1367   *color   = ParseColorName(e);\r
1368 }\r
1369 \r
1370 void\r
1371 ParseTextAttribs(ColorClass cc, char *s)\r
1372 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1373     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1374     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1375 }\r
1376 \r
1377 void\r
1378 ParseBoardSize(void *addr, char *name)\r
1379 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1380   BoardSize bs = SizeTiny;\r
1381   while (sizeInfo[bs].name != NULL) {\r
1382     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1383         *(BoardSize *)addr = bs;\r
1384         return;\r
1385     }\r
1386     bs++;\r
1387   }\r
1388   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1389 }\r
1390 \r
1391 void\r
1392 LoadAllSounds()\r
1393 { // [HGM] import name from appData first\r
1394   ColorClass cc;\r
1395   SoundClass sc;\r
1396   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1397     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1398     textAttribs[cc].sound.data = NULL;\r
1399     MyLoadSound(&textAttribs[cc].sound);\r
1400   }\r
1401   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1402     textAttribs[cc].sound.name = strdup("");\r
1403     textAttribs[cc].sound.data = NULL;\r
1404   }\r
1405   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1406     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1407     sounds[sc].data = NULL;\r
1408     MyLoadSound(&sounds[sc]);\r
1409   }\r
1410 }\r
1411 \r
1412 void\r
1413 SetCommPortDefaults()\r
1414 {\r
1415    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1416   dcb.DCBlength = sizeof(DCB);\r
1417   dcb.BaudRate = 9600;\r
1418   dcb.fBinary = TRUE;\r
1419   dcb.fParity = FALSE;\r
1420   dcb.fOutxCtsFlow = FALSE;\r
1421   dcb.fOutxDsrFlow = FALSE;\r
1422   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1423   dcb.fDsrSensitivity = FALSE;\r
1424   dcb.fTXContinueOnXoff = TRUE;\r
1425   dcb.fOutX = FALSE;\r
1426   dcb.fInX = FALSE;\r
1427   dcb.fNull = FALSE;\r
1428   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1429   dcb.fAbortOnError = FALSE;\r
1430   dcb.ByteSize = 7;\r
1431   dcb.Parity = SPACEPARITY;\r
1432   dcb.StopBits = ONESTOPBIT;\r
1433 }\r
1434 \r
1435 // [HGM] args: these three cases taken out to stay in front-end\r
1436 void\r
1437 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1438 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1439         // while the curent board size determines the element. This system should be ported to XBoard.\r
1440         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1441         int bs;\r
1442         for (bs=0; bs<NUM_SIZES; bs++) {\r
1443           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1444           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1445           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1446             ad->argName, mfp->faceName, mfp->pointSize,\r
1447             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1448             mfp->bold ? "b" : "",\r
1449             mfp->italic ? "i" : "",\r
1450             mfp->underline ? "u" : "",\r
1451             mfp->strikeout ? "s" : "",\r
1452             (int)mfp->charset);\r
1453         }\r
1454       }\r
1455 \r
1456 void\r
1457 ExportSounds()\r
1458 { // [HGM] copy the names from the internal WB variables to appData\r
1459   ColorClass cc;\r
1460   SoundClass sc;\r
1461   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1462     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1463   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1464     (&appData.soundMove)[sc] = sounds[sc].name;\r
1465 }\r
1466 \r
1467 void\r
1468 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1469 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1470         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1471         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1472           (ta->effects & CFE_BOLD) ? "b" : "",\r
1473           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1474           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1475           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1476           (ta->effects) ? " " : "",\r
1477           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1478       }\r
1479 \r
1480 void\r
1481 SaveColor(FILE *f, ArgDescriptor *ad)\r
1482 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1483         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1484         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1485           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1486 }\r
1487 \r
1488 void\r
1489 SaveBoardSize(FILE *f, char *name, void *addr)\r
1490 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1491   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1492 }\r
1493 \r
1494 void\r
1495 ParseCommPortSettings(char *s)\r
1496 { // wrapper to keep dcb from back-end\r
1497   ParseCommSettings(s, &dcb);\r
1498 }\r
1499 \r
1500 void\r
1501 GetWindowCoords()\r
1502 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1503   GetActualPlacement(hwndMain, &wpMain);\r
1504   GetActualPlacement(hwndConsole, &wpConsole);\r
1505   GetActualPlacement(commentDialog, &wpComment);\r
1506   GetActualPlacement(editTagsDialog, &wpTags);\r
1507   GetActualPlacement(gameListDialog, &wpGameList);\r
1508   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1509   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1510   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1511 }\r
1512 \r
1513 void\r
1514 PrintCommPortSettings(FILE *f, char *name)\r
1515 { // wrapper to shield back-end from DCB\r
1516       PrintCommSettings(f, name, &dcb);\r
1517 }\r
1518 \r
1519 int\r
1520 MySearchPath(char *installDir, char *name, char *fullname)\r
1521 {\r
1522   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1523   if(name[0]== '%') {\r
1524     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1525     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1526       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1527       *strchr(buf, '%') = 0;\r
1528       strcat(fullname, getenv(buf));\r
1529       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1530     }\r
1531     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1532     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1533     return (int) strlen(fullname);\r
1534   }\r
1535   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1536 }\r
1537 \r
1538 int\r
1539 MyGetFullPathName(char *name, char *fullname)\r
1540 {\r
1541   char *dummy;\r
1542   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1543 }\r
1544 \r
1545 int\r
1546 MainWindowUp()\r
1547 { // [HGM] args: allows testing if main window is realized from back-end\r
1548   return hwndMain != NULL;\r
1549 }\r
1550 \r
1551 void\r
1552 PopUpStartupDialog()\r
1553 {\r
1554     FARPROC lpProc;\r
1555     \r
1556     LoadLanguageFile(appData.language);\r
1557     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1558     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1559     FreeProcInstance(lpProc);\r
1560 }\r
1561 \r
1562 /*---------------------------------------------------------------------------*\\r
1563  *\r
1564  * GDI board drawing routines\r
1565  *\r
1566 \*---------------------------------------------------------------------------*/\r
1567 \r
1568 /* [AS] Draw square using background texture */\r
1569 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1570 {\r
1571     XFORM   x;\r
1572 \r
1573     if( mode == 0 ) {\r
1574         return; /* Should never happen! */\r
1575     }\r
1576 \r
1577     SetGraphicsMode( dst, GM_ADVANCED );\r
1578 \r
1579     switch( mode ) {\r
1580     case 1:\r
1581         /* Identity */\r
1582         break;\r
1583     case 2:\r
1584         /* X reflection */\r
1585         x.eM11 = -1.0;\r
1586         x.eM12 = 0;\r
1587         x.eM21 = 0;\r
1588         x.eM22 = 1.0;\r
1589         x.eDx = (FLOAT) dw + dx - 1;\r
1590         x.eDy = 0;\r
1591         dx = 0;\r
1592         SetWorldTransform( dst, &x );\r
1593         break;\r
1594     case 3:\r
1595         /* Y reflection */\r
1596         x.eM11 = 1.0;\r
1597         x.eM12 = 0;\r
1598         x.eM21 = 0;\r
1599         x.eM22 = -1.0;\r
1600         x.eDx = 0;\r
1601         x.eDy = (FLOAT) dh + dy - 1;\r
1602         dy = 0;\r
1603         SetWorldTransform( dst, &x );\r
1604         break;\r
1605     case 4:\r
1606         /* X/Y flip */\r
1607         x.eM11 = 0;\r
1608         x.eM12 = 1.0;\r
1609         x.eM21 = 1.0;\r
1610         x.eM22 = 0;\r
1611         x.eDx = (FLOAT) dx;\r
1612         x.eDy = (FLOAT) dy;\r
1613         dx = 0;\r
1614         dy = 0;\r
1615         SetWorldTransform( dst, &x );\r
1616         break;\r
1617     }\r
1618 \r
1619     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1620 \r
1621     x.eM11 = 1.0;\r
1622     x.eM12 = 0;\r
1623     x.eM21 = 0;\r
1624     x.eM22 = 1.0;\r
1625     x.eDx = 0;\r
1626     x.eDy = 0;\r
1627     SetWorldTransform( dst, &x );\r
1628 \r
1629     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1630 }\r
1631 \r
1632 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1633 enum {\r
1634     PM_WP = (int) WhitePawn, \r
1635     PM_WN = (int) WhiteKnight, \r
1636     PM_WB = (int) WhiteBishop, \r
1637     PM_WR = (int) WhiteRook, \r
1638     PM_WQ = (int) WhiteQueen, \r
1639     PM_WF = (int) WhiteFerz, \r
1640     PM_WW = (int) WhiteWazir, \r
1641     PM_WE = (int) WhiteAlfil, \r
1642     PM_WM = (int) WhiteMan, \r
1643     PM_WO = (int) WhiteCannon, \r
1644     PM_WU = (int) WhiteUnicorn, \r
1645     PM_WH = (int) WhiteNightrider, \r
1646     PM_WA = (int) WhiteAngel, \r
1647     PM_WC = (int) WhiteMarshall, \r
1648     PM_WAB = (int) WhiteCardinal, \r
1649     PM_WD = (int) WhiteDragon, \r
1650     PM_WL = (int) WhiteLance, \r
1651     PM_WS = (int) WhiteCobra, \r
1652     PM_WV = (int) WhiteFalcon, \r
1653     PM_WSG = (int) WhiteSilver, \r
1654     PM_WG = (int) WhiteGrasshopper, \r
1655     PM_WK = (int) WhiteKing,\r
1656     PM_BP = (int) BlackPawn, \r
1657     PM_BN = (int) BlackKnight, \r
1658     PM_BB = (int) BlackBishop, \r
1659     PM_BR = (int) BlackRook, \r
1660     PM_BQ = (int) BlackQueen, \r
1661     PM_BF = (int) BlackFerz, \r
1662     PM_BW = (int) BlackWazir, \r
1663     PM_BE = (int) BlackAlfil, \r
1664     PM_BM = (int) BlackMan,\r
1665     PM_BO = (int) BlackCannon, \r
1666     PM_BU = (int) BlackUnicorn, \r
1667     PM_BH = (int) BlackNightrider, \r
1668     PM_BA = (int) BlackAngel, \r
1669     PM_BC = (int) BlackMarshall, \r
1670     PM_BG = (int) BlackGrasshopper, \r
1671     PM_BAB = (int) BlackCardinal,\r
1672     PM_BD = (int) BlackDragon,\r
1673     PM_BL = (int) BlackLance,\r
1674     PM_BS = (int) BlackCobra,\r
1675     PM_BV = (int) BlackFalcon,\r
1676     PM_BSG = (int) BlackSilver,\r
1677     PM_BK = (int) BlackKing\r
1678 };\r
1679 \r
1680 static HFONT hPieceFont = NULL;\r
1681 static HBITMAP hPieceMask[(int) EmptySquare];\r
1682 static HBITMAP hPieceFace[(int) EmptySquare];\r
1683 static int fontBitmapSquareSize = 0;\r
1684 static char pieceToFontChar[(int) EmptySquare] =\r
1685                               { 'p', 'n', 'b', 'r', 'q', \r
1686                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1687                       'k', 'o', 'm', 'v', 't', 'w', \r
1688                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1689                                                               'l' };\r
1690 \r
1691 extern BOOL SetCharTable( char *table, const char * map );\r
1692 /* [HGM] moved to backend.c */\r
1693 \r
1694 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1695 {\r
1696     HBRUSH hbrush;\r
1697     BYTE r1 = GetRValue( color );\r
1698     BYTE g1 = GetGValue( color );\r
1699     BYTE b1 = GetBValue( color );\r
1700     BYTE r2 = r1 / 2;\r
1701     BYTE g2 = g1 / 2;\r
1702     BYTE b2 = b1 / 2;\r
1703     RECT rc;\r
1704 \r
1705     /* Create a uniform background first */\r
1706     hbrush = CreateSolidBrush( color );\r
1707     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1708     FillRect( hdc, &rc, hbrush );\r
1709     DeleteObject( hbrush );\r
1710     \r
1711     if( mode == 1 ) {\r
1712         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1713         int steps = squareSize / 2;\r
1714         int i;\r
1715 \r
1716         for( i=0; i<steps; i++ ) {\r
1717             BYTE r = r1 - (r1-r2) * i / steps;\r
1718             BYTE g = g1 - (g1-g2) * i / steps;\r
1719             BYTE b = b1 - (b1-b2) * i / steps;\r
1720 \r
1721             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1722             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1723             FillRect( hdc, &rc, hbrush );\r
1724             DeleteObject(hbrush);\r
1725         }\r
1726     }\r
1727     else if( mode == 2 ) {\r
1728         /* Diagonal gradient, good more or less for every piece */\r
1729         POINT triangle[3];\r
1730         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1731         HBRUSH hbrush_old;\r
1732         int steps = squareSize;\r
1733         int i;\r
1734 \r
1735         triangle[0].x = squareSize - steps;\r
1736         triangle[0].y = squareSize;\r
1737         triangle[1].x = squareSize;\r
1738         triangle[1].y = squareSize;\r
1739         triangle[2].x = squareSize;\r
1740         triangle[2].y = squareSize - steps;\r
1741 \r
1742         for( i=0; i<steps; i++ ) {\r
1743             BYTE r = r1 - (r1-r2) * i / steps;\r
1744             BYTE g = g1 - (g1-g2) * i / steps;\r
1745             BYTE b = b1 - (b1-b2) * i / steps;\r
1746 \r
1747             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1748             hbrush_old = SelectObject( hdc, hbrush );\r
1749             Polygon( hdc, triangle, 3 );\r
1750             SelectObject( hdc, hbrush_old );\r
1751             DeleteObject(hbrush);\r
1752             triangle[0].x++;\r
1753             triangle[2].y++;\r
1754         }\r
1755 \r
1756         SelectObject( hdc, hpen );\r
1757     }\r
1758 }\r
1759 \r
1760 /*\r
1761     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1762     seems to work ok. The main problem here is to find the "inside" of a chess\r
1763     piece: follow the steps as explained below.\r
1764 */\r
1765 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1766 {\r
1767     HBITMAP hbm;\r
1768     HBITMAP hbm_old;\r
1769     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1770     RECT rc;\r
1771     SIZE sz;\r
1772     POINT pt;\r
1773     int backColor = whitePieceColor; \r
1774     int foreColor = blackPieceColor;\r
1775     \r
1776     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1777         backColor = appData.fontBackColorWhite;\r
1778         foreColor = appData.fontForeColorWhite;\r
1779     }\r
1780     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1781         backColor = appData.fontBackColorBlack;\r
1782         foreColor = appData.fontForeColorBlack;\r
1783     }\r
1784 \r
1785     /* Mask */\r
1786     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1787 \r
1788     hbm_old = SelectObject( hdc, hbm );\r
1789 \r
1790     rc.left = 0;\r
1791     rc.top = 0;\r
1792     rc.right = squareSize;\r
1793     rc.bottom = squareSize;\r
1794 \r
1795     /* Step 1: background is now black */\r
1796     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1797 \r
1798     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1799 \r
1800     pt.x = (squareSize - sz.cx) / 2;\r
1801     pt.y = (squareSize - sz.cy) / 2;\r
1802 \r
1803     SetBkMode( hdc, TRANSPARENT );\r
1804     SetTextColor( hdc, chroma );\r
1805     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1806     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1807 \r
1808     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1809     /* Step 3: the area outside the piece is filled with white */\r
1810 //    FloodFill( hdc, 0, 0, chroma );\r
1811     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1812     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1813     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1814     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1815     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1816     /* \r
1817         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1818         but if the start point is not inside the piece we're lost!\r
1819         There should be a better way to do this... if we could create a region or path\r
1820         from the fill operation we would be fine for example.\r
1821     */\r
1822 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1823     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1824 \r
1825     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1826         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1827         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1828 \r
1829         SelectObject( dc2, bm2 );\r
1830         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1831         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1832         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1833         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1834         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1835 \r
1836         DeleteDC( dc2 );\r
1837         DeleteObject( bm2 );\r
1838     }\r
1839 \r
1840     SetTextColor( hdc, 0 );\r
1841     /* \r
1842         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1843         draw the piece again in black for safety.\r
1844     */\r
1845     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1846 \r
1847     SelectObject( hdc, hbm_old );\r
1848 \r
1849     if( hPieceMask[index] != NULL ) {\r
1850         DeleteObject( hPieceMask[index] );\r
1851     }\r
1852 \r
1853     hPieceMask[index] = hbm;\r
1854 \r
1855     /* Face */\r
1856     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1857 \r
1858     SelectObject( hdc, hbm );\r
1859 \r
1860     {\r
1861         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1862         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1863         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1864 \r
1865         SelectObject( dc1, hPieceMask[index] );\r
1866         SelectObject( dc2, bm2 );\r
1867         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1868         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1869         \r
1870         /* \r
1871             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1872             the piece background and deletes (makes transparent) the rest.\r
1873             Thanks to that mask, we are free to paint the background with the greates\r
1874             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1875             We use this, to make gradients and give the pieces a "roundish" look.\r
1876         */\r
1877         SetPieceBackground( hdc, backColor, 2 );\r
1878         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1879 \r
1880         DeleteDC( dc2 );\r
1881         DeleteDC( dc1 );\r
1882         DeleteObject( bm2 );\r
1883     }\r
1884 \r
1885     SetTextColor( hdc, foreColor );\r
1886     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1887 \r
1888     SelectObject( hdc, hbm_old );\r
1889 \r
1890     if( hPieceFace[index] != NULL ) {\r
1891         DeleteObject( hPieceFace[index] );\r
1892     }\r
1893 \r
1894     hPieceFace[index] = hbm;\r
1895 }\r
1896 \r
1897 static int TranslatePieceToFontPiece( int piece )\r
1898 {\r
1899     switch( piece ) {\r
1900     case BlackPawn:\r
1901         return PM_BP;\r
1902     case BlackKnight:\r
1903         return PM_BN;\r
1904     case BlackBishop:\r
1905         return PM_BB;\r
1906     case BlackRook:\r
1907         return PM_BR;\r
1908     case BlackQueen:\r
1909         return PM_BQ;\r
1910     case BlackKing:\r
1911         return PM_BK;\r
1912     case WhitePawn:\r
1913         return PM_WP;\r
1914     case WhiteKnight:\r
1915         return PM_WN;\r
1916     case WhiteBishop:\r
1917         return PM_WB;\r
1918     case WhiteRook:\r
1919         return PM_WR;\r
1920     case WhiteQueen:\r
1921         return PM_WQ;\r
1922     case WhiteKing:\r
1923         return PM_WK;\r
1924 \r
1925     case BlackAngel:\r
1926         return PM_BA;\r
1927     case BlackMarshall:\r
1928         return PM_BC;\r
1929     case BlackFerz:\r
1930         return PM_BF;\r
1931     case BlackNightrider:\r
1932         return PM_BH;\r
1933     case BlackAlfil:\r
1934         return PM_BE;\r
1935     case BlackWazir:\r
1936         return PM_BW;\r
1937     case BlackUnicorn:\r
1938         return PM_BU;\r
1939     case BlackCannon:\r
1940         return PM_BO;\r
1941     case BlackGrasshopper:\r
1942         return PM_BG;\r
1943     case BlackMan:\r
1944         return PM_BM;\r
1945     case BlackSilver:\r
1946         return PM_BSG;\r
1947     case BlackLance:\r
1948         return PM_BL;\r
1949     case BlackFalcon:\r
1950         return PM_BV;\r
1951     case BlackCobra:\r
1952         return PM_BS;\r
1953     case BlackCardinal:\r
1954         return PM_BAB;\r
1955     case BlackDragon:\r
1956         return PM_BD;\r
1957 \r
1958     case WhiteAngel:\r
1959         return PM_WA;\r
1960     case WhiteMarshall:\r
1961         return PM_WC;\r
1962     case WhiteFerz:\r
1963         return PM_WF;\r
1964     case WhiteNightrider:\r
1965         return PM_WH;\r
1966     case WhiteAlfil:\r
1967         return PM_WE;\r
1968     case WhiteWazir:\r
1969         return PM_WW;\r
1970     case WhiteUnicorn:\r
1971         return PM_WU;\r
1972     case WhiteCannon:\r
1973         return PM_WO;\r
1974     case WhiteGrasshopper:\r
1975         return PM_WG;\r
1976     case WhiteMan:\r
1977         return PM_WM;\r
1978     case WhiteSilver:\r
1979         return PM_WSG;\r
1980     case WhiteLance:\r
1981         return PM_WL;\r
1982     case WhiteFalcon:\r
1983         return PM_WV;\r
1984     case WhiteCobra:\r
1985         return PM_WS;\r
1986     case WhiteCardinal:\r
1987         return PM_WAB;\r
1988     case WhiteDragon:\r
1989         return PM_WD;\r
1990     }\r
1991 \r
1992     return 0;\r
1993 }\r
1994 \r
1995 void CreatePiecesFromFont()\r
1996 {\r
1997     LOGFONT lf;\r
1998     HDC hdc_window = NULL;\r
1999     HDC hdc = NULL;\r
2000     HFONT hfont_old;\r
2001     int fontHeight;\r
2002     int i;\r
2003 \r
2004     if( fontBitmapSquareSize < 0 ) {\r
2005         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2006         return;\r
2007     }\r
2008 \r
2009     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2010             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2011         fontBitmapSquareSize = -1;\r
2012         return;\r
2013     }\r
2014 \r
2015     if( fontBitmapSquareSize != squareSize ) {\r
2016         hdc_window = GetDC( hwndMain );\r
2017         hdc = CreateCompatibleDC( hdc_window );\r
2018 \r
2019         if( hPieceFont != NULL ) {\r
2020             DeleteObject( hPieceFont );\r
2021         }\r
2022         else {\r
2023             for( i=0; i<=(int)BlackKing; i++ ) {\r
2024                 hPieceMask[i] = NULL;\r
2025                 hPieceFace[i] = NULL;\r
2026             }\r
2027         }\r
2028 \r
2029         fontHeight = 75;\r
2030 \r
2031         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2032             fontHeight = appData.fontPieceSize;\r
2033         }\r
2034 \r
2035         fontHeight = (fontHeight * squareSize) / 100;\r
2036 \r
2037         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2038         lf.lfWidth = 0;\r
2039         lf.lfEscapement = 0;\r
2040         lf.lfOrientation = 0;\r
2041         lf.lfWeight = FW_NORMAL;\r
2042         lf.lfItalic = 0;\r
2043         lf.lfUnderline = 0;\r
2044         lf.lfStrikeOut = 0;\r
2045         lf.lfCharSet = DEFAULT_CHARSET;\r
2046         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2047         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2048         lf.lfQuality = PROOF_QUALITY;\r
2049         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2050         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2051         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2052 \r
2053         hPieceFont = CreateFontIndirect( &lf );\r
2054 \r
2055         if( hPieceFont == NULL ) {\r
2056             fontBitmapSquareSize = -2;\r
2057         }\r
2058         else {\r
2059             /* Setup font-to-piece character table */\r
2060             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2061                 /* No (or wrong) global settings, try to detect the font */\r
2062                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2063                     /* Alpha */\r
2064                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2065                 }\r
2066                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2067                     /* DiagramTT* family */\r
2068                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2069                 }\r
2070                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2071                     /* Fairy symbols */\r
2072                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2073                 }\r
2074                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2075                     /* Good Companion (Some characters get warped as literal :-( */\r
2076                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2077                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2078                     SetCharTable(pieceToFontChar, s);\r
2079                 }\r
2080                 else {\r
2081                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2082                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2083                 }\r
2084             }\r
2085 \r
2086             /* Create bitmaps */\r
2087             hfont_old = SelectObject( hdc, hPieceFont );\r
2088             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2089                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2090                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2091 \r
2092             SelectObject( hdc, hfont_old );\r
2093 \r
2094             fontBitmapSquareSize = squareSize;\r
2095         }\r
2096     }\r
2097 \r
2098     if( hdc != NULL ) {\r
2099         DeleteDC( hdc );\r
2100     }\r
2101 \r
2102     if( hdc_window != NULL ) {\r
2103         ReleaseDC( hwndMain, hdc_window );\r
2104     }\r
2105 }\r
2106 \r
2107 HBITMAP\r
2108 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2109 {\r
2110   char name[128];\r
2111 \r
2112     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2113   if (gameInfo.event &&\r
2114       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2115       strcmp(name, "k80s") == 0) {\r
2116     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2117   }\r
2118   return LoadBitmap(hinst, name);\r
2119 }\r
2120 \r
2121 \r
2122 /* Insert a color into the program's logical palette\r
2123    structure.  This code assumes the given color is\r
2124    the result of the RGB or PALETTERGB macro, and it\r
2125    knows how those macros work (which is documented).\r
2126 */\r
2127 VOID\r
2128 InsertInPalette(COLORREF color)\r
2129 {\r
2130   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2131 \r
2132   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2133     DisplayFatalError(_("Too many colors"), 0, 1);\r
2134     pLogPal->palNumEntries--;\r
2135     return;\r
2136   }\r
2137 \r
2138   pe->peFlags = (char) 0;\r
2139   pe->peRed = (char) (0xFF & color);\r
2140   pe->peGreen = (char) (0xFF & (color >> 8));\r
2141   pe->peBlue = (char) (0xFF & (color >> 16));\r
2142   return;\r
2143 }\r
2144 \r
2145 \r
2146 VOID\r
2147 InitDrawingColors()\r
2148 {\r
2149   if (pLogPal == NULL) {\r
2150     /* Allocate enough memory for a logical palette with\r
2151      * PALETTESIZE entries and set the size and version fields\r
2152      * of the logical palette structure.\r
2153      */\r
2154     pLogPal = (NPLOGPALETTE)\r
2155       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2156                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2157     pLogPal->palVersion    = 0x300;\r
2158   }\r
2159   pLogPal->palNumEntries = 0;\r
2160 \r
2161   InsertInPalette(lightSquareColor);\r
2162   InsertInPalette(darkSquareColor);\r
2163   InsertInPalette(whitePieceColor);\r
2164   InsertInPalette(blackPieceColor);\r
2165   InsertInPalette(highlightSquareColor);\r
2166   InsertInPalette(premoveHighlightColor);\r
2167 \r
2168   /*  create a logical color palette according the information\r
2169    *  in the LOGPALETTE structure.\r
2170    */\r
2171   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2172 \r
2173   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2174   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2175   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2176   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2177   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2178   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2179   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2180   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2181   /* [AS] Force rendering of the font-based pieces */\r
2182   if( fontBitmapSquareSize > 0 ) {\r
2183     fontBitmapSquareSize = 0;\r
2184   }\r
2185 }\r
2186 \r
2187 \r
2188 int\r
2189 BoardWidth(int boardSize, int n)\r
2190 { /* [HGM] argument n added to allow different width and height */\r
2191   int lineGap = sizeInfo[boardSize].lineGap;\r
2192 \r
2193   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2194       lineGap = appData.overrideLineGap;\r
2195   }\r
2196 \r
2197   return (n + 1) * lineGap +\r
2198           n * sizeInfo[boardSize].squareSize;\r
2199 }\r
2200 \r
2201 /* Respond to board resize by dragging edge */\r
2202 VOID\r
2203 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2204 {\r
2205   BoardSize newSize = NUM_SIZES - 1;\r
2206   static int recurse = 0;\r
2207   if (IsIconic(hwndMain)) return;\r
2208   if (recurse > 0) return;\r
2209   recurse++;\r
2210   while (newSize > 0) {\r
2211         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2212         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2213            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2214     newSize--;\r
2215   } \r
2216   boardSize = newSize;\r
2217   InitDrawingSizes(boardSize, flags);\r
2218   recurse--;\r
2219 }\r
2220 \r
2221 \r
2222 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2223 \r
2224 VOID\r
2225 InitDrawingSizes(BoardSize boardSize, int flags)\r
2226 {\r
2227   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2228   ChessSquare piece;\r
2229   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2230   HDC hdc;\r
2231   SIZE clockSize, messageSize;\r
2232   HFONT oldFont;\r
2233   char buf[MSG_SIZ];\r
2234   char *str;\r
2235   HMENU hmenu = GetMenu(hwndMain);\r
2236   RECT crect, wrect, oldRect;\r
2237   int offby;\r
2238   LOGBRUSH logbrush;\r
2239 \r
2240   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2241   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2242 \r
2243   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2244   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2245 \r
2246   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2247   oldRect.top = wpMain.y;\r
2248   oldRect.right = wpMain.x + wpMain.width;\r
2249   oldRect.bottom = wpMain.y + wpMain.height;\r
2250 \r
2251   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2252   smallLayout = sizeInfo[boardSize].smallLayout;\r
2253   squareSize = sizeInfo[boardSize].squareSize;\r
2254   lineGap = sizeInfo[boardSize].lineGap;\r
2255   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2256 \r
2257   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2258       lineGap = appData.overrideLineGap;\r
2259   }\r
2260 \r
2261   if (tinyLayout != oldTinyLayout) {\r
2262     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2263     if (tinyLayout) {\r
2264       style &= ~WS_SYSMENU;\r
2265       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2266                  "&Minimize\tCtrl+F4");\r
2267     } else {\r
2268       style |= WS_SYSMENU;\r
2269       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2270     }\r
2271     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2272 \r
2273     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2274       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2275         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2276     }\r
2277     DrawMenuBar(hwndMain);\r
2278   }\r
2279 \r
2280   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2281   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2282 \r
2283   /* Get text area sizes */\r
2284   hdc = GetDC(hwndMain);\r
2285   if (appData.clockMode) {\r
2286     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2287   } else {\r
2288     snprintf(buf, MSG_SIZ, _("White"));\r
2289   }\r
2290   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2291   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2292   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2293   str = _("We only care about the height here");\r
2294   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2295   SelectObject(hdc, oldFont);\r
2296   ReleaseDC(hwndMain, hdc);\r
2297 \r
2298   /* Compute where everything goes */\r
2299   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2300         /* [HGM] logo: if either logo is on, reserve space for it */\r
2301         logoHeight =  2*clockSize.cy;\r
2302         leftLogoRect.left   = OUTER_MARGIN;\r
2303         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2304         leftLogoRect.top    = OUTER_MARGIN;\r
2305         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2306 \r
2307         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2308         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2309         rightLogoRect.top    = OUTER_MARGIN;\r
2310         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2311 \r
2312 \r
2313     whiteRect.left = leftLogoRect.right;\r
2314     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2315     whiteRect.top = OUTER_MARGIN;\r
2316     whiteRect.bottom = whiteRect.top + logoHeight;\r
2317 \r
2318     blackRect.right = rightLogoRect.left;\r
2319     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2320     blackRect.top = whiteRect.top;\r
2321     blackRect.bottom = whiteRect.bottom;\r
2322   } else {\r
2323     whiteRect.left = OUTER_MARGIN;\r
2324     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2325     whiteRect.top = OUTER_MARGIN;\r
2326     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2327 \r
2328     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2329     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2330     blackRect.top = whiteRect.top;\r
2331     blackRect.bottom = whiteRect.bottom;\r
2332 \r
2333     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2334   }\r
2335 \r
2336   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2337   if (appData.showButtonBar) {\r
2338     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2339       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2340   } else {\r
2341     messageRect.right = OUTER_MARGIN + boardWidth;\r
2342   }\r
2343   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2344   messageRect.bottom = messageRect.top + messageSize.cy;\r
2345 \r
2346   boardRect.left = OUTER_MARGIN;\r
2347   boardRect.right = boardRect.left + boardWidth;\r
2348   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2349   boardRect.bottom = boardRect.top + boardHeight;\r
2350 \r
2351   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2352   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2353   oldBoardSize = boardSize;\r
2354   oldTinyLayout = tinyLayout;\r
2355   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2356   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2357     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2358   winW *= 1 + twoBoards;\r
2359   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2360   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2361   wpMain.height = winH; //       without disturbing window attachments\r
2362   GetWindowRect(hwndMain, &wrect);\r
2363   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2364                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2365 \r
2366   // [HGM] placement: let attached windows follow size change.\r
2367   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2368   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2369   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2370   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2371   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2372 \r
2373   /* compensate if menu bar wrapped */\r
2374   GetClientRect(hwndMain, &crect);\r
2375   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2376   wpMain.height += offby;\r
2377   switch (flags) {\r
2378   case WMSZ_TOPLEFT:\r
2379     SetWindowPos(hwndMain, NULL, \r
2380                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2381                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2382     break;\r
2383 \r
2384   case WMSZ_TOPRIGHT:\r
2385   case WMSZ_TOP:\r
2386     SetWindowPos(hwndMain, NULL, \r
2387                  wrect.left, wrect.bottom - wpMain.height, \r
2388                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2389     break;\r
2390 \r
2391   case WMSZ_BOTTOMLEFT:\r
2392   case WMSZ_LEFT:\r
2393     SetWindowPos(hwndMain, NULL, \r
2394                  wrect.right - wpMain.width, wrect.top, \r
2395                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2396     break;\r
2397 \r
2398   case WMSZ_BOTTOMRIGHT:\r
2399   case WMSZ_BOTTOM:\r
2400   case WMSZ_RIGHT:\r
2401   default:\r
2402     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2403                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2404     break;\r
2405   }\r
2406 \r
2407   hwndPause = NULL;\r
2408   for (i = 0; i < N_BUTTONS; i++) {\r
2409     if (buttonDesc[i].hwnd != NULL) {\r
2410       DestroyWindow(buttonDesc[i].hwnd);\r
2411       buttonDesc[i].hwnd = NULL;\r
2412     }\r
2413     if (appData.showButtonBar) {\r
2414       buttonDesc[i].hwnd =\r
2415         CreateWindow("BUTTON", buttonDesc[i].label,\r
2416                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2417                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2418                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2419                      (HMENU) buttonDesc[i].id,\r
2420                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2421       if (tinyLayout) {\r
2422         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2423                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2424                     MAKELPARAM(FALSE, 0));\r
2425       }\r
2426       if (buttonDesc[i].id == IDM_Pause)\r
2427         hwndPause = buttonDesc[i].hwnd;\r
2428       buttonDesc[i].wndproc = (WNDPROC)\r
2429         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2430     }\r
2431   }\r
2432   if (gridPen != NULL) DeleteObject(gridPen);\r
2433   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2434   if (premovePen != NULL) DeleteObject(premovePen);\r
2435   if (lineGap != 0) {\r
2436     logbrush.lbStyle = BS_SOLID;\r
2437     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2438     gridPen =\r
2439       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2440                    lineGap, &logbrush, 0, NULL);\r
2441     logbrush.lbColor = highlightSquareColor;\r
2442     highlightPen =\r
2443       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2444                    lineGap, &logbrush, 0, NULL);\r
2445 \r
2446     logbrush.lbColor = premoveHighlightColor; \r
2447     premovePen =\r
2448       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2449                    lineGap, &logbrush, 0, NULL);\r
2450 \r
2451     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2452     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2453       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2454       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2455         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2456       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2457         BOARD_WIDTH * (squareSize + lineGap);\r
2458       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2459     }\r
2460     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2461       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2462       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2463         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2464         lineGap / 2 + (i * (squareSize + lineGap));\r
2465       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2466         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2467       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2468     }\r
2469   }\r
2470 \r
2471   /* [HGM] Licensing requirement */\r
2472 #ifdef GOTHIC\r
2473   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2474 #endif\r
2475 #ifdef FALCON\r
2476   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2477 #endif\r
2478   GothicPopUp( "", VariantNormal);\r
2479 \r
2480 \r
2481 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2482 \r
2483   /* Load piece bitmaps for this board size */\r
2484   for (i=0; i<=2; i++) {\r
2485     for (piece = WhitePawn;\r
2486          (int) piece < (int) BlackPawn;\r
2487          piece = (ChessSquare) ((int) piece + 1)) {\r
2488       if (pieceBitmap[i][piece] != NULL)\r
2489         DeleteObject(pieceBitmap[i][piece]);\r
2490     }\r
2491   }\r
2492 \r
2493   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2494   // Orthodox Chess pieces\r
2495   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2496   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2497   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2498   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2499   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2500   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2501   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2502   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2503   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2504   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2505   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2506   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2507   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2508   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2509   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2510   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2511     // in Shogi, Hijack the unused Queen for Lance\r
2512     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2513     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2514     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2515   } else {\r
2516     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2517     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2518     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2519   }\r
2520 \r
2521   if(squareSize <= 72 && squareSize >= 33) { \r
2522     /* A & C are available in most sizes now */\r
2523     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2524       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2525       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2526       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2527       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2528       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2529       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2530       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2531       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2532       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2533       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2534       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2535       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2536     } else { // Smirf-like\r
2537       if(gameInfo.variant == VariantSChess) {\r
2538         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2539         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2540         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2541       } else {\r
2542         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2543         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2544         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2545       }\r
2546     }\r
2547     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2548       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2549       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2550       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2551     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2552       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2553       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2554       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2555     } else { // WinBoard standard\r
2556       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2557       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2558       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2559     }\r
2560   }\r
2561 \r
2562 \r
2563   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2564     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2565     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2566     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2567     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2568     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2569     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2570     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2571     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2572     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2573     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2574     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2575     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2576     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2577     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2578     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2579     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2580     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2581     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2582     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2583     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2584     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2585     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2586     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2587     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2588     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2589     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2590     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2591     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2592     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2593     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2594 \r
2595     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2596       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2597       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2598       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2599       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2600       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2601       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2602       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2603       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2604       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2605       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2606       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2607       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2608     } else {\r
2609       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2610       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2611       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2612       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2613       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2614       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2615       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2616       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2617       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2618       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2619       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2620       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2621     }\r
2622 \r
2623   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2624     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2625     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2626     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2627     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2628     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2629     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2630     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2631     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2632     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2633     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2634     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2635     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2636     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2637     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2638   }\r
2639 \r
2640 \r
2641   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2642   /* special Shogi support in this size */\r
2643   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2644       for (piece = WhitePawn;\r
2645            (int) piece < (int) BlackPawn;\r
2646            piece = (ChessSquare) ((int) piece + 1)) {\r
2647         if (pieceBitmap[i][piece] != NULL)\r
2648           DeleteObject(pieceBitmap[i][piece]);\r
2649       }\r
2650     }\r
2651   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2652   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2653   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2654   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2655   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2656   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2657   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2658   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2659   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2660   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2661   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2662   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2663   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2664   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2665   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2666   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2667   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2668   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2669   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2670   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2671   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2672   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2673   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2674   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2675   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2676   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2677   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2678   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2679   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2680   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2681   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2682   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2683   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2684   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2685   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2686   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2687   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2688   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2689   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2690   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2691   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2692   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2693   minorSize = 0;\r
2694   }\r
2695 }\r
2696 \r
2697 HBITMAP\r
2698 PieceBitmap(ChessSquare p, int kind)\r
2699 {\r
2700   if ((int) p >= (int) BlackPawn)\r
2701     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2702 \r
2703   return pieceBitmap[kind][(int) p];\r
2704 }\r
2705 \r
2706 /***************************************************************/\r
2707 \r
2708 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2709 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2710 /*\r
2711 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2712 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2713 */\r
2714 \r
2715 VOID\r
2716 SquareToPos(int row, int column, int * x, int * y)\r
2717 {\r
2718   if (flipView) {\r
2719     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2720     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2721   } else {\r
2722     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2723     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2724   }\r
2725 }\r
2726 \r
2727 VOID\r
2728 DrawCoordsOnDC(HDC hdc)\r
2729 {\r
2730   static char files[] = "0123456789012345678901221098765432109876543210";\r
2731   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2732   char str[2] = { NULLCHAR, NULLCHAR };\r
2733   int oldMode, oldAlign, x, y, start, i;\r
2734   HFONT oldFont;\r
2735   HBRUSH oldBrush;\r
2736 \r
2737   if (!appData.showCoords)\r
2738     return;\r
2739 \r
2740   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2741 \r
2742   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2743   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2744   oldAlign = GetTextAlign(hdc);\r
2745   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2746 \r
2747   y = boardRect.top + lineGap;\r
2748   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2749 \r
2750   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2751   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2752     str[0] = files[start + i];\r
2753     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2754     y += squareSize + lineGap;\r
2755   }\r
2756 \r
2757   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2758 \r
2759   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2760   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2761     str[0] = ranks[start + i];\r
2762     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2763     x += squareSize + lineGap;\r
2764   }    \r
2765 \r
2766   SelectObject(hdc, oldBrush);\r
2767   SetBkMode(hdc, oldMode);\r
2768   SetTextAlign(hdc, oldAlign);\r
2769   SelectObject(hdc, oldFont);\r
2770 }\r
2771 \r
2772 VOID\r
2773 DrawGridOnDC(HDC hdc)\r
2774 {\r
2775   HPEN oldPen;\r
2776  \r
2777   if (lineGap != 0) {\r
2778     oldPen = SelectObject(hdc, gridPen);\r
2779     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2780     SelectObject(hdc, oldPen);\r
2781   }\r
2782 }\r
2783 \r
2784 #define HIGHLIGHT_PEN 0\r
2785 #define PREMOVE_PEN   1\r
2786 \r
2787 VOID\r
2788 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2789 {\r
2790   int x1, y1;\r
2791   HPEN oldPen, hPen;\r
2792   if (lineGap == 0) return;\r
2793   if (flipView) {\r
2794     x1 = boardRect.left +\r
2795       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2796     y1 = boardRect.top +\r
2797       lineGap/2 + y * (squareSize + lineGap);\r
2798   } else {\r
2799     x1 = boardRect.left +\r
2800       lineGap/2 + x * (squareSize + lineGap);\r
2801     y1 = boardRect.top +\r
2802       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2803   }\r
2804   hPen = pen ? premovePen : highlightPen;\r
2805   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2806   MoveToEx(hdc, x1, y1, NULL);\r
2807   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2808   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2809   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2810   LineTo(hdc, x1, y1);\r
2811   SelectObject(hdc, oldPen);\r
2812 }\r
2813 \r
2814 VOID\r
2815 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2816 {\r
2817   int i;\r
2818   for (i=0; i<2; i++) {\r
2819     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2820       DrawHighlightOnDC(hdc, TRUE,\r
2821                         h->sq[i].x, h->sq[i].y,\r
2822                         pen);\r
2823   }\r
2824 }\r
2825 \r
2826 /* Note: sqcolor is used only in monoMode */\r
2827 /* Note that this code is largely duplicated in woptions.c,\r
2828    function DrawSampleSquare, so that needs to be updated too */\r
2829 VOID\r
2830 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2831 {\r
2832   HBITMAP oldBitmap;\r
2833   HBRUSH oldBrush;\r
2834   int tmpSize;\r
2835 \r
2836   if (appData.blindfold) return;\r
2837 \r
2838   /* [AS] Use font-based pieces if needed */\r
2839   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2840     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2841     CreatePiecesFromFont();\r
2842 \r
2843     if( fontBitmapSquareSize == squareSize ) {\r
2844         int index = TranslatePieceToFontPiece(piece);\r
2845 \r
2846         SelectObject( tmphdc, hPieceMask[ index ] );\r
2847 \r
2848       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2849         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2850       else\r
2851         BitBlt( hdc,\r
2852             x, y,\r
2853             squareSize, squareSize,\r
2854             tmphdc,\r
2855             0, 0,\r
2856             SRCAND );\r
2857 \r
2858         SelectObject( tmphdc, hPieceFace[ index ] );\r
2859 \r
2860       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2861         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2862       else\r
2863         BitBlt( hdc,\r
2864             x, y,\r
2865             squareSize, squareSize,\r
2866             tmphdc,\r
2867             0, 0,\r
2868             SRCPAINT );\r
2869 \r
2870         return;\r
2871     }\r
2872   }\r
2873 \r
2874   if (appData.monoMode) {\r
2875     SelectObject(tmphdc, PieceBitmap(piece, \r
2876       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2877     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2878            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2879   } else {\r
2880     tmpSize = squareSize;\r
2881     if(minorSize &&\r
2882         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2883          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2884       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2885       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2886       x += (squareSize - minorSize)>>1;\r
2887       y += squareSize - minorSize - 2;\r
2888       tmpSize = minorSize;\r
2889     }\r
2890     if (color || appData.allWhite ) {\r
2891       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2892       if( color )\r
2893               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2894       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2895       if(appData.upsideDown && color==flipView)\r
2896         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2897       else\r
2898         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2899       /* Use black for outline of white pieces */\r
2900       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2901       if(appData.upsideDown && color==flipView)\r
2902         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2903       else\r
2904         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2905     } else {\r
2906       /* Use square color for details of black pieces */\r
2907       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2908       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2909       if(appData.upsideDown && !flipView)\r
2910         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2911       else\r
2912         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2913     }\r
2914     SelectObject(hdc, oldBrush);\r
2915     SelectObject(tmphdc, oldBitmap);\r
2916   }\r
2917 }\r
2918 \r
2919 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2920 int GetBackTextureMode( int algo )\r
2921 {\r
2922     int result = BACK_TEXTURE_MODE_DISABLED;\r
2923 \r
2924     switch( algo ) \r
2925     {\r
2926         case BACK_TEXTURE_MODE_PLAIN:\r
2927             result = 1; /* Always use identity map */\r
2928             break;\r
2929         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2930             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2931             break;\r
2932     }\r
2933 \r
2934     return result;\r
2935 }\r
2936 \r
2937 /* \r
2938     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2939     to handle redraws cleanly (as random numbers would always be different).\r
2940 */\r
2941 VOID RebuildTextureSquareInfo()\r
2942 {\r
2943     BITMAP bi;\r
2944     int lite_w = 0;\r
2945     int lite_h = 0;\r
2946     int dark_w = 0;\r
2947     int dark_h = 0;\r
2948     int row;\r
2949     int col;\r
2950 \r
2951     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2952 \r
2953     if( liteBackTexture != NULL ) {\r
2954         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2955             lite_w = bi.bmWidth;\r
2956             lite_h = bi.bmHeight;\r
2957         }\r
2958     }\r
2959 \r
2960     if( darkBackTexture != NULL ) {\r
2961         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2962             dark_w = bi.bmWidth;\r
2963             dark_h = bi.bmHeight;\r
2964         }\r
2965     }\r
2966 \r
2967     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2968         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2969             if( (col + row) & 1 ) {\r
2970                 /* Lite square */\r
2971                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2972                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2973                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2974                   else\r
2975                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2976                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2977                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2978                   else\r
2979                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2980                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2981                 }\r
2982             }\r
2983             else {\r
2984                 /* Dark square */\r
2985                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2986                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2987                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2988                   else\r
2989                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2990                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2991                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2992                   else\r
2993                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2994                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2995                 }\r
2996             }\r
2997         }\r
2998     }\r
2999 }\r
3000 \r
3001 /* [AS] Arrow highlighting support */\r
3002 \r
3003 static double A_WIDTH = 5; /* Width of arrow body */\r
3004 \r
3005 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3006 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3007 \r
3008 static double Sqr( double x )\r
3009 {\r
3010     return x*x;\r
3011 }\r
3012 \r
3013 static int Round( double x )\r
3014 {\r
3015     return (int) (x + 0.5);\r
3016 }\r
3017 \r
3018 /* Draw an arrow between two points using current settings */\r
3019 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3020 {\r
3021     POINT arrow[7];\r
3022     double dx, dy, j, k, x, y;\r
3023 \r
3024     if( d_x == s_x ) {\r
3025         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3026 \r
3027         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3028         arrow[0].y = s_y;\r
3029 \r
3030         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3031         arrow[1].y = d_y - h;\r
3032 \r
3033         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3034         arrow[2].y = d_y - h;\r
3035 \r
3036         arrow[3].x = d_x;\r
3037         arrow[3].y = d_y;\r
3038 \r
3039         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3040         arrow[5].y = d_y - h;\r
3041 \r
3042         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3043         arrow[4].y = d_y - h;\r
3044 \r
3045         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3046         arrow[6].y = s_y;\r
3047     }\r
3048     else if( d_y == s_y ) {\r
3049         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3050 \r
3051         arrow[0].x = s_x;\r
3052         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3053 \r
3054         arrow[1].x = d_x - w;\r
3055         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3056 \r
3057         arrow[2].x = d_x - w;\r
3058         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3059 \r
3060         arrow[3].x = d_x;\r
3061         arrow[3].y = d_y;\r
3062 \r
3063         arrow[5].x = d_x - w;\r
3064         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3065 \r
3066         arrow[4].x = d_x - w;\r
3067         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3068 \r
3069         arrow[6].x = s_x;\r
3070         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3071     }\r
3072     else {\r
3073         /* [AS] Needed a lot of paper for this! :-) */\r
3074         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3075         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3076   \r
3077         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3078 \r
3079         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3080 \r
3081         x = s_x;\r
3082         y = s_y;\r
3083 \r
3084         arrow[0].x = Round(x - j);\r
3085         arrow[0].y = Round(y + j*dx);\r
3086 \r
3087         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3088         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3089 \r
3090         if( d_x > s_x ) {\r
3091             x = (double) d_x - k;\r
3092             y = (double) d_y - k*dy;\r
3093         }\r
3094         else {\r
3095             x = (double) d_x + k;\r
3096             y = (double) d_y + k*dy;\r
3097         }\r
3098 \r
3099         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3100 \r
3101         arrow[6].x = Round(x - j);\r
3102         arrow[6].y = Round(y + j*dx);\r
3103 \r
3104         arrow[2].x = Round(arrow[6].x + 2*j);\r
3105         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3106 \r
3107         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3108         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3109 \r
3110         arrow[4].x = d_x;\r
3111         arrow[4].y = d_y;\r
3112 \r
3113         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3114         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3115     }\r
3116 \r
3117     Polygon( hdc, arrow, 7 );\r
3118 }\r
3119 \r
3120 /* [AS] Draw an arrow between two squares */\r
3121 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3122 {\r
3123     int s_x, s_y, d_x, d_y;\r
3124     HPEN hpen;\r
3125     HPEN holdpen;\r
3126     HBRUSH hbrush;\r
3127     HBRUSH holdbrush;\r
3128     LOGBRUSH stLB;\r
3129 \r
3130     if( s_col == d_col && s_row == d_row ) {\r
3131         return;\r
3132     }\r
3133 \r
3134     /* Get source and destination points */\r
3135     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3136     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3137 \r
3138     if( d_y > s_y ) {\r
3139         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3140     }\r
3141     else if( d_y < s_y ) {\r
3142         d_y += squareSize / 2 + squareSize / 4;\r
3143     }\r
3144     else {\r
3145         d_y += squareSize / 2;\r
3146     }\r
3147 \r
3148     if( d_x > s_x ) {\r
3149         d_x += squareSize / 2 - squareSize / 4;\r
3150     }\r
3151     else if( d_x < s_x ) {\r
3152         d_x += squareSize / 2 + squareSize / 4;\r
3153     }\r
3154     else {\r
3155         d_x += squareSize / 2;\r
3156     }\r
3157 \r
3158     s_x += squareSize / 2;\r
3159     s_y += squareSize / 2;\r
3160 \r
3161     /* Adjust width */\r
3162     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3163 \r
3164     /* Draw */\r
3165     stLB.lbStyle = BS_SOLID;\r
3166     stLB.lbColor = appData.highlightArrowColor;\r
3167     stLB.lbHatch = 0;\r
3168 \r
3169     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3170     holdpen = SelectObject( hdc, hpen );\r
3171     hbrush = CreateBrushIndirect( &stLB );\r
3172     holdbrush = SelectObject( hdc, hbrush );\r
3173 \r
3174     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3175 \r
3176     SelectObject( hdc, holdpen );\r
3177     SelectObject( hdc, holdbrush );\r
3178     DeleteObject( hpen );\r
3179     DeleteObject( hbrush );\r
3180 }\r
3181 \r
3182 BOOL HasHighlightInfo()\r
3183 {\r
3184     BOOL result = FALSE;\r
3185 \r
3186     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3187         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3188     {\r
3189         result = TRUE;\r
3190     }\r
3191 \r
3192     return result;\r
3193 }\r
3194 \r
3195 BOOL IsDrawArrowEnabled()\r
3196 {\r
3197     BOOL result = FALSE;\r
3198 \r
3199     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3200         result = TRUE;\r
3201     }\r
3202 \r
3203     return result;\r
3204 }\r
3205 \r
3206 VOID DrawArrowHighlight( HDC hdc )\r
3207 {\r
3208     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3209         DrawArrowBetweenSquares( hdc,\r
3210             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3211             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3212     }\r
3213 }\r
3214 \r
3215 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3216 {\r
3217     HRGN result = NULL;\r
3218 \r
3219     if( HasHighlightInfo() ) {\r
3220         int x1, y1, x2, y2;\r
3221         int sx, sy, dx, dy;\r
3222 \r
3223         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3224         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3225 \r
3226         sx = MIN( x1, x2 );\r
3227         sy = MIN( y1, y2 );\r
3228         dx = MAX( x1, x2 ) + squareSize;\r
3229         dy = MAX( y1, y2 ) + squareSize;\r
3230 \r
3231         result = CreateRectRgn( sx, sy, dx, dy );\r
3232     }\r
3233 \r
3234     return result;\r
3235 }\r
3236 \r
3237 /*\r
3238     Warning: this function modifies the behavior of several other functions. \r
3239     \r
3240     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3241     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3242     repaint is scattered all over the place, which is not good for features such as\r
3243     "arrow highlighting" that require a full repaint of the board.\r
3244 \r
3245     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3246     user interaction, when speed is not so important) but especially to avoid errors\r
3247     in the displayed graphics.\r
3248 \r
3249     In such patched places, I always try refer to this function so there is a single\r
3250     place to maintain knowledge.\r
3251     \r
3252     To restore the original behavior, just return FALSE unconditionally.\r
3253 */\r
3254 BOOL IsFullRepaintPreferrable()\r
3255 {\r
3256     BOOL result = FALSE;\r
3257 \r
3258     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3259         /* Arrow may appear on the board */\r
3260         result = TRUE;\r
3261     }\r
3262 \r
3263     return result;\r
3264 }\r
3265 \r
3266 /* \r
3267     This function is called by DrawPosition to know whether a full repaint must\r
3268     be forced or not.\r
3269 \r
3270     Only DrawPosition may directly call this function, which makes use of \r
3271     some state information. Other function should call DrawPosition specifying \r
3272     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3273 */\r
3274 BOOL DrawPositionNeedsFullRepaint()\r
3275 {\r
3276     BOOL result = FALSE;\r
3277 \r
3278     /* \r
3279         Probably a slightly better policy would be to trigger a full repaint\r
3280         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3281         but animation is fast enough that it's difficult to notice.\r
3282     */\r
3283     if( animInfo.piece == EmptySquare ) {\r
3284         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3285             result = TRUE;\r
3286         }\r
3287     }\r
3288 \r
3289     return result;\r
3290 }\r
3291 \r
3292 VOID\r
3293 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3294 {\r
3295   int row, column, x, y, square_color, piece_color;\r
3296   ChessSquare piece;\r
3297   HBRUSH oldBrush;\r
3298   HDC texture_hdc = NULL;\r
3299 \r
3300   /* [AS] Initialize background textures if needed */\r
3301   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3302       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3303       if( backTextureSquareSize != squareSize \r
3304        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3305           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3306           backTextureSquareSize = squareSize;\r
3307           RebuildTextureSquareInfo();\r
3308       }\r
3309 \r
3310       texture_hdc = CreateCompatibleDC( hdc );\r
3311   }\r
3312 \r
3313   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3314     for (column = 0; column < BOARD_WIDTH; column++) {\r
3315   \r
3316       SquareToPos(row, column, &x, &y);\r
3317 \r
3318       piece = board[row][column];\r
3319 \r
3320       square_color = ((column + row) % 2) == 1;\r
3321       if( gameInfo.variant == VariantXiangqi ) {\r
3322           square_color = !InPalace(row, column);\r
3323           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3324           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3325       }\r
3326       piece_color = (int) piece < (int) BlackPawn;\r
3327 \r
3328 \r
3329       /* [HGM] holdings file: light square or black */\r
3330       if(column == BOARD_LEFT-2) {\r
3331             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3332                 square_color = 1;\r
3333             else {\r
3334                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3335                 continue;\r
3336             }\r
3337       } else\r
3338       if(column == BOARD_RGHT + 1 ) {\r
3339             if( row < gameInfo.holdingsSize )\r
3340                 square_color = 1;\r
3341             else {\r
3342                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3343                 continue;\r
3344             }\r
3345       }\r
3346       if(column == BOARD_LEFT-1 ) /* left align */\r
3347             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3348       else if( column == BOARD_RGHT) /* right align */\r
3349             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3350       else\r
3351       if (appData.monoMode) {\r
3352         if (piece == EmptySquare) {\r
3353           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3354                  square_color ? WHITENESS : BLACKNESS);\r
3355         } else {\r
3356           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3357         }\r
3358       } \r
3359       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3360           /* [AS] Draw the square using a texture bitmap */\r
3361           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3362           int r = row, c = column; // [HGM] do not flip board in flipView\r
3363           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3364 \r
3365           DrawTile( x, y, \r
3366               squareSize, squareSize, \r
3367               hdc, \r
3368               texture_hdc,\r
3369               backTextureSquareInfo[r][c].mode,\r
3370               backTextureSquareInfo[r][c].x,\r
3371               backTextureSquareInfo[r][c].y );\r
3372 \r
3373           SelectObject( texture_hdc, hbm );\r
3374 \r
3375           if (piece != EmptySquare) {\r
3376               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3377           }\r
3378       }\r
3379       else {\r
3380         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3381 \r
3382         oldBrush = SelectObject(hdc, brush );\r
3383         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3384         SelectObject(hdc, oldBrush);\r
3385         if (piece != EmptySquare)\r
3386           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3387       }\r
3388     }\r
3389   }\r
3390 \r
3391   if( texture_hdc != NULL ) {\r
3392     DeleteDC( texture_hdc );\r
3393   }\r
3394 }\r
3395 \r
3396 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3397 void fputDW(FILE *f, int x)\r
3398 {\r
3399         fputc(x     & 255, f);\r
3400         fputc(x>>8  & 255, f);\r
3401         fputc(x>>16 & 255, f);\r
3402         fputc(x>>24 & 255, f);\r
3403 }\r
3404 \r
3405 #define MAX_CLIPS 200   /* more than enough */\r
3406 \r
3407 VOID\r
3408 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3409 {\r
3410 //  HBITMAP bufferBitmap;\r
3411   BITMAP bi;\r
3412 //  RECT Rect;\r
3413   HDC tmphdc;\r
3414   HBITMAP hbm;\r
3415   int w = 100, h = 50;\r
3416 \r
3417   if(logo == NULL) {\r
3418     if(!logoHeight) return;\r
3419     FillRect( hdc, &logoRect, whitePieceBrush );\r
3420   }\r
3421 //  GetClientRect(hwndMain, &Rect);\r
3422 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3423 //                                      Rect.bottom-Rect.top+1);\r
3424   tmphdc = CreateCompatibleDC(hdc);\r
3425   hbm = SelectObject(tmphdc, logo);\r
3426   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3427             w = bi.bmWidth;\r
3428             h = bi.bmHeight;\r
3429   }\r
3430   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3431                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3432   SelectObject(tmphdc, hbm);\r
3433   DeleteDC(tmphdc);\r
3434 }\r
3435 \r
3436 VOID\r
3437 DisplayLogos()\r
3438 {\r
3439   if(logoHeight) {\r
3440         HDC hdc = GetDC(hwndMain);\r
3441         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3442         if(appData.autoLogo) {\r
3443           \r
3444           switch(gameMode) { // pick logos based on game mode\r
3445             case IcsObserving:\r
3446                 whiteLogo = second.programLogo; // ICS logo\r
3447                 blackLogo = second.programLogo;\r
3448             default:\r
3449                 break;\r
3450             case IcsPlayingWhite:\r
3451                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3452                 blackLogo = second.programLogo; // ICS logo\r
3453                 break;\r
3454             case IcsPlayingBlack:\r
3455                 whiteLogo = second.programLogo; // ICS logo\r
3456                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3457                 break;\r
3458             case TwoMachinesPlay:\r
3459                 if(first.twoMachinesColor[0] == 'b') {\r
3460                     whiteLogo = second.programLogo;\r
3461                     blackLogo = first.programLogo;\r
3462                 }\r
3463                 break;\r
3464             case MachinePlaysWhite:\r
3465                 blackLogo = userLogo;\r
3466                 break;\r
3467             case MachinePlaysBlack:\r
3468                 whiteLogo = userLogo;\r
3469                 blackLogo = first.programLogo;\r
3470           }\r
3471         }\r
3472         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3473         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3474         ReleaseDC(hwndMain, hdc);\r
3475   }\r
3476 }\r
3477 \r
3478 void\r
3479 UpdateLogos(int display)\r
3480 { // called after loading new engine(s), in tourney or from menu\r
3481   LoadLogo(&first, 0, FALSE);\r
3482   LoadLogo(&second, 1, appData.icsActive);\r
3483   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3484   if(display) DisplayLogos();\r
3485 }\r
3486 \r
3487 static HDC hdcSeek;\r
3488 \r
3489 // [HGM] seekgraph\r
3490 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3491 {\r
3492     POINT stPt;\r
3493     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3494     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3495     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3496     SelectObject( hdcSeek, hp );\r
3497 }\r
3498 \r
3499 // front-end wrapper for drawing functions to do rectangles\r
3500 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3501 {\r
3502     HPEN hp;\r
3503     RECT rc;\r
3504 \r
3505     if (hdcSeek == NULL) {\r
3506     hdcSeek = GetDC(hwndMain);\r
3507       if (!appData.monoMode) {\r
3508         SelectPalette(hdcSeek, hPal, FALSE);\r
3509         RealizePalette(hdcSeek);\r
3510       }\r
3511     }\r
3512     hp = SelectObject( hdcSeek, gridPen );\r
3513     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3514     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3515     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3516     SelectObject( hdcSeek, hp );\r
3517 }\r
3518 \r
3519 // front-end wrapper for putting text in graph\r
3520 void DrawSeekText(char *buf, int x, int y)\r
3521 {\r
3522         SIZE stSize;\r
3523         SetBkMode( hdcSeek, TRANSPARENT );\r
3524         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3525         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3526 }\r
3527 \r
3528 void DrawSeekDot(int x, int y, int color)\r
3529 {\r
3530         int square = color & 0x80;\r
3531         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3532                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3533         color &= 0x7F;\r
3534         if(square)\r
3535             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3536                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3537         else\r
3538             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3539                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3540             SelectObject(hdcSeek, oldBrush);\r
3541 }\r
3542 \r
3543 VOID\r
3544 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3545 {\r
3546   static Board lastReq[2], lastDrawn[2];\r
3547   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3548   static int lastDrawnFlipView = 0;\r
3549   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3550   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3551   HDC tmphdc;\r
3552   HDC hdcmem;\r
3553   HBITMAP bufferBitmap;\r
3554   HBITMAP oldBitmap;\r
3555   RECT Rect;\r
3556   HRGN clips[MAX_CLIPS];\r
3557   ChessSquare dragged_piece = EmptySquare;\r
3558   int nr = twoBoards*partnerUp;\r
3559 \r
3560   /* I'm undecided on this - this function figures out whether a full\r
3561    * repaint is necessary on its own, so there's no real reason to have the\r
3562    * caller tell it that.  I think this can safely be set to FALSE - but\r
3563    * if we trust the callers not to request full repaints unnessesarily, then\r
3564    * we could skip some clipping work.  In other words, only request a full\r
3565    * redraw when the majority of pieces have changed positions (ie. flip, \r
3566    * gamestart and similar)  --Hawk\r
3567    */\r
3568   Boolean fullrepaint = repaint;\r
3569 \r
3570   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3571 \r
3572   if( DrawPositionNeedsFullRepaint() ) {\r
3573       fullrepaint = TRUE;\r
3574   }\r
3575 \r
3576   if (board == NULL) {\r
3577     if (!lastReqValid[nr]) {\r
3578       return;\r
3579     }\r
3580     board = lastReq[nr];\r
3581   } else {\r
3582     CopyBoard(lastReq[nr], board);\r
3583     lastReqValid[nr] = 1;\r
3584   }\r
3585 \r
3586   if (doingSizing) {\r
3587     return;\r
3588   }\r
3589 \r
3590   if (IsIconic(hwndMain)) {\r
3591     return;\r
3592   }\r
3593 \r
3594   if (hdc == NULL) {\r
3595     hdc = GetDC(hwndMain);\r
3596     if (!appData.monoMode) {\r
3597       SelectPalette(hdc, hPal, FALSE);\r
3598       RealizePalette(hdc);\r
3599     }\r
3600     releaseDC = TRUE;\r
3601   } else {\r
3602     releaseDC = FALSE;\r
3603   }\r
3604 \r
3605   /* Create some work-DCs */\r
3606   hdcmem = CreateCompatibleDC(hdc);\r
3607   tmphdc = CreateCompatibleDC(hdc);\r
3608 \r
3609   /* If dragging is in progress, we temporarely remove the piece */\r
3610   /* [HGM] or temporarily decrease count if stacked              */\r
3611   /*       !! Moved to before board compare !!                   */\r
3612   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3613     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3614     if(dragInfo.from.x == BOARD_LEFT-2 ) {\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     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3619             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3620         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3621     } else \r
3622         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3623   }\r
3624 \r
3625   /* Figure out which squares need updating by comparing the \r
3626    * newest board with the last drawn board and checking if\r
3627    * flipping has changed.\r
3628    */\r
3629   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3630     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3631       for (column = 0; column < BOARD_WIDTH; column++) {\r
3632         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3633           SquareToPos(row, column, &x, &y);\r
3634           clips[num_clips++] =\r
3635             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3636         }\r
3637       }\r
3638     }\r
3639    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3640     for (i=0; i<2; i++) {\r
3641       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3642           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3643         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3644             lastDrawnHighlight.sq[i].y >= 0) {\r
3645           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3646                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3647           clips[num_clips++] =\r
3648             CreateRectRgn(x - lineGap, y - lineGap, \r
3649                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3650         }\r
3651         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3652           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3653           clips[num_clips++] =\r
3654             CreateRectRgn(x - lineGap, y - lineGap, \r
3655                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3656         }\r
3657       }\r
3658     }\r
3659     for (i=0; i<2; i++) {\r
3660       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3661           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3662         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3663             lastDrawnPremove.sq[i].y >= 0) {\r
3664           SquareToPos(lastDrawnPremove.sq[i].y,\r
3665                       lastDrawnPremove.sq[i].x, &x, &y);\r
3666           clips[num_clips++] =\r
3667             CreateRectRgn(x - lineGap, y - lineGap, \r
3668                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3669         }\r
3670         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3671             premoveHighlightInfo.sq[i].y >= 0) {\r
3672           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3673                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3674           clips[num_clips++] =\r
3675             CreateRectRgn(x - lineGap, y - lineGap, \r
3676                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3677         }\r
3678       }\r
3679     }\r
3680    } else { // nr == 1\r
3681         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3682         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3683         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3684         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3685       for (i=0; i<2; i++) {\r
3686         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3687             partnerHighlightInfo.sq[i].y >= 0) {\r
3688           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3689                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3690           clips[num_clips++] =\r
3691             CreateRectRgn(x - lineGap, y - lineGap, \r
3692                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3693         }\r
3694         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3695             oldPartnerHighlight.sq[i].y >= 0) {\r
3696           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3697                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3698           clips[num_clips++] =\r
3699             CreateRectRgn(x - lineGap, y - lineGap, \r
3700                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3701         }\r
3702       }\r
3703    }\r
3704   } else {\r
3705     fullrepaint = TRUE;\r
3706   }\r
3707 \r
3708   /* Create a buffer bitmap - this is the actual bitmap\r
3709    * being written to.  When all the work is done, we can\r
3710    * copy it to the real DC (the screen).  This avoids\r
3711    * the problems with flickering.\r
3712    */\r
3713   GetClientRect(hwndMain, &Rect);\r
3714   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3715                                         Rect.bottom-Rect.top+1);\r
3716   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3717   if (!appData.monoMode) {\r
3718     SelectPalette(hdcmem, hPal, FALSE);\r
3719   }\r
3720 \r
3721   /* Create clips for dragging */\r
3722   if (!fullrepaint) {\r
3723     if (dragInfo.from.x >= 0) {\r
3724       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3725       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3726     }\r
3727     if (dragInfo.start.x >= 0) {\r
3728       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3729       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3730     }\r
3731     if (dragInfo.pos.x >= 0) {\r
3732       x = dragInfo.pos.x - squareSize / 2;\r
3733       y = dragInfo.pos.y - squareSize / 2;\r
3734       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3735     }\r
3736     if (dragInfo.lastpos.x >= 0) {\r
3737       x = dragInfo.lastpos.x - squareSize / 2;\r
3738       y = dragInfo.lastpos.y - squareSize / 2;\r
3739       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3740     }\r
3741   }\r
3742 \r
3743   /* Are we animating a move?  \r
3744    * If so, \r
3745    *   - remove the piece from the board (temporarely)\r
3746    *   - calculate the clipping region\r
3747    */\r
3748   if (!fullrepaint) {\r
3749     if (animInfo.piece != EmptySquare) {\r
3750       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3751       x = boardRect.left + animInfo.lastpos.x;\r
3752       y = boardRect.top + animInfo.lastpos.y;\r
3753       x2 = boardRect.left + animInfo.pos.x;\r
3754       y2 = boardRect.top + animInfo.pos.y;\r
3755       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3756       /* Slight kludge.  The real problem is that after AnimateMove is\r
3757          done, the position on the screen does not match lastDrawn.\r
3758          This currently causes trouble only on e.p. captures in\r
3759          atomic, where the piece moves to an empty square and then\r
3760          explodes.  The old and new positions both had an empty square\r
3761          at the destination, but animation has drawn a piece there and\r
3762          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3763       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3764     }\r
3765   }\r
3766 \r
3767   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3768   if (num_clips == 0)\r
3769     fullrepaint = TRUE;\r
3770 \r
3771   /* Set clipping on the memory DC */\r
3772   if (!fullrepaint) {\r
3773     SelectClipRgn(hdcmem, clips[0]);\r
3774     for (x = 1; x < num_clips; x++) {\r
3775       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3776         abort();  // this should never ever happen!\r
3777     }\r
3778   }\r
3779 \r
3780   /* Do all the drawing to the memory DC */\r
3781   if(explodeInfo.radius) { // [HGM] atomic\r
3782         HBRUSH oldBrush;\r
3783         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3784         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3785         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3786         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3787         x += squareSize/2;\r
3788         y += squareSize/2;\r
3789         if(!fullrepaint) {\r
3790           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3791           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3792         }\r
3793         DrawGridOnDC(hdcmem);\r
3794         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3795         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3796         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3797         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3798         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3799         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3800         SelectObject(hdcmem, oldBrush);\r
3801   } else {\r
3802     DrawGridOnDC(hdcmem);\r
3803     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3804         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3805         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3806     } else {\r
3807         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3808         oldPartnerHighlight = partnerHighlightInfo;\r
3809     }\r
3810     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3811   }\r
3812   if(nr == 0) // [HGM] dual: markers only on left board\r
3813   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3814     for (column = 0; column < BOARD_WIDTH; column++) {\r
3815         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3816             HBRUSH oldBrush = SelectObject(hdcmem, \r
3817                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3818             SquareToPos(row, column, &x, &y);\r
3819             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3820                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3821             SelectObject(hdcmem, oldBrush);\r
3822         }\r
3823     }\r
3824   }\r
3825 \r
3826   if( appData.highlightMoveWithArrow ) {\r
3827     DrawArrowHighlight(hdcmem);\r
3828   }\r
3829 \r
3830   DrawCoordsOnDC(hdcmem);\r
3831 \r
3832   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3833                  /* to make sure lastDrawn contains what is actually drawn */\r
3834 \r
3835   /* Put the dragged piece back into place and draw it (out of place!) */\r
3836     if (dragged_piece != EmptySquare) {\r
3837     /* [HGM] or restack */\r
3838     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3839                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3840     else\r
3841     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3842                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3843     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3844     x = dragInfo.pos.x - squareSize / 2;\r
3845     y = dragInfo.pos.y - squareSize / 2;\r
3846     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3847                   ((int) dragInfo.piece < (int) BlackPawn), \r
3848                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3849   }   \r
3850   \r
3851   /* Put the animated piece back into place and draw it */\r
3852   if (animInfo.piece != EmptySquare) {\r
3853     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3854     x = boardRect.left + animInfo.pos.x;\r
3855     y = boardRect.top + animInfo.pos.y;\r
3856     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3857                   ((int) animInfo.piece < (int) BlackPawn),\r
3858                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3859   }\r
3860 \r
3861   /* Release the bufferBitmap by selecting in the old bitmap \r
3862    * and delete the memory DC\r
3863    */\r
3864   SelectObject(hdcmem, oldBitmap);\r
3865   DeleteDC(hdcmem);\r
3866 \r
3867   /* Set clipping on the target DC */\r
3868   if (!fullrepaint) {\r
3869     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3870         RECT rect;\r
3871         GetRgnBox(clips[x], &rect);\r
3872         DeleteObject(clips[x]);\r
3873         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3874                           rect.right + wpMain.width/2, rect.bottom);\r
3875     }\r
3876     SelectClipRgn(hdc, clips[0]);\r
3877     for (x = 1; x < num_clips; x++) {\r
3878       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3879         abort();   // this should never ever happen!\r
3880     } \r
3881   }\r
3882 \r
3883   /* Copy the new bitmap onto the screen in one go.\r
3884    * This way we avoid any flickering\r
3885    */\r
3886   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3887   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3888          boardRect.right - boardRect.left,\r
3889          boardRect.bottom - boardRect.top,\r
3890          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3891   if(saveDiagFlag) { \r
3892     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3893     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3894 \r
3895     GetObject(bufferBitmap, sizeof(b), &b);\r
3896     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3897         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3898         bih.biWidth = b.bmWidth;\r
3899         bih.biHeight = b.bmHeight;\r
3900         bih.biPlanes = 1;\r
3901         bih.biBitCount = b.bmBitsPixel;\r
3902         bih.biCompression = 0;\r
3903         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3904         bih.biXPelsPerMeter = 0;\r
3905         bih.biYPelsPerMeter = 0;\r
3906         bih.biClrUsed = 0;\r
3907         bih.biClrImportant = 0;\r
3908 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3909 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3910         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3911 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3912 \r
3913         wb = b.bmWidthBytes;\r
3914         // count colors\r
3915         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3916                 int k = ((int*) pData)[i];\r
3917                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3918                 if(j >= 16) break;\r
3919                 color[j] = k;\r
3920                 if(j >= nrColors) nrColors = j+1;\r
3921         }\r
3922         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3923                 INT p = 0;\r
3924                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3925                     for(w=0; w<(wb>>2); w+=2) {\r
3926                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3927                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3928                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3929                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3930                         pData[p++] = m | j<<4;\r
3931                     }\r
3932                     while(p&3) pData[p++] = 0;\r
3933                 }\r
3934                 fac = 3;\r
3935                 wb = ((wb+31)>>5)<<2;\r
3936         }\r
3937         // write BITMAPFILEHEADER\r
3938         fprintf(diagFile, "BM");\r
3939         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3940         fputDW(diagFile, 0);\r
3941         fputDW(diagFile, 0x36 + (fac?64:0));\r
3942         // write BITMAPINFOHEADER\r
3943         fputDW(diagFile, 40);\r
3944         fputDW(diagFile, b.bmWidth);\r
3945         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3946         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3947         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3948         fputDW(diagFile, 0);\r
3949         fputDW(diagFile, 0);\r
3950         fputDW(diagFile, 0);\r
3951         fputDW(diagFile, 0);\r
3952         fputDW(diagFile, 0);\r
3953         fputDW(diagFile, 0);\r
3954         // write color table\r
3955         if(fac)\r
3956         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3957         // write bitmap data\r
3958         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3959                 fputc(pData[i], diagFile);\r
3960         free(pData);\r
3961      }\r
3962   }\r
3963 \r
3964   SelectObject(tmphdc, oldBitmap);\r
3965 \r
3966   /* Massive cleanup */\r
3967   for (x = 0; x < num_clips; x++)\r
3968     DeleteObject(clips[x]);\r
3969 \r
3970   DeleteDC(tmphdc);\r
3971   DeleteObject(bufferBitmap);\r
3972 \r
3973   if (releaseDC) \r
3974     ReleaseDC(hwndMain, hdc);\r
3975   \r
3976   if (lastDrawnFlipView != flipView && nr == 0) {\r
3977     if (flipView)\r
3978       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3979     else\r
3980       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3981   }\r
3982 \r
3983 /*  CopyBoard(lastDrawn, board);*/\r
3984   lastDrawnHighlight = highlightInfo;\r
3985   lastDrawnPremove   = premoveHighlightInfo;\r
3986   lastDrawnFlipView = flipView;\r
3987   lastDrawnValid[nr] = 1;\r
3988 }\r
3989 \r
3990 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3991 int\r
3992 SaveDiagram(f)\r
3993      FILE *f;\r
3994 {\r
3995     saveDiagFlag = 1; diagFile = f;\r
3996     HDCDrawPosition(NULL, TRUE, NULL);\r
3997     saveDiagFlag = 0;\r
3998 \r
3999     fclose(f);\r
4000     return TRUE;\r
4001 }\r
4002 \r
4003 \r
4004 /*---------------------------------------------------------------------------*\\r
4005 | CLIENT PAINT PROCEDURE\r
4006 |   This is the main event-handler for the WM_PAINT message.\r
4007 |\r
4008 \*---------------------------------------------------------------------------*/\r
4009 VOID\r
4010 PaintProc(HWND hwnd)\r
4011 {\r
4012   HDC         hdc;\r
4013   PAINTSTRUCT ps;\r
4014   HFONT       oldFont;\r
4015 \r
4016   if((hdc = BeginPaint(hwnd, &ps))) {\r
4017     if (IsIconic(hwnd)) {\r
4018       DrawIcon(hdc, 2, 2, iconCurrent);\r
4019     } else {\r
4020       if (!appData.monoMode) {\r
4021         SelectPalette(hdc, hPal, FALSE);\r
4022         RealizePalette(hdc);\r
4023       }\r
4024       HDCDrawPosition(hdc, 1, NULL);\r
4025       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4026         flipView = !flipView; partnerUp = !partnerUp;\r
4027         HDCDrawPosition(hdc, 1, NULL);\r
4028         flipView = !flipView; partnerUp = !partnerUp;\r
4029       }\r
4030       oldFont =\r
4031         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4032       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4033                  ETO_CLIPPED|ETO_OPAQUE,\r
4034                  &messageRect, messageText, strlen(messageText), NULL);\r
4035       SelectObject(hdc, oldFont);\r
4036       DisplayBothClocks();\r
4037       DisplayLogos();\r
4038     }\r
4039     EndPaint(hwnd,&ps);\r
4040   }\r
4041 \r
4042   return;\r
4043 }\r
4044 \r
4045 \r
4046 /*\r
4047  * If the user selects on a border boundary, return -1; if off the board,\r
4048  *   return -2.  Otherwise map the event coordinate to the square.\r
4049  * The offset boardRect.left or boardRect.top must already have been\r
4050  *   subtracted from x.\r
4051  */\r
4052 int EventToSquare(x, limit)\r
4053      int x, limit;\r
4054 {\r
4055   if (x <= 0)\r
4056     return -2;\r
4057   if (x < lineGap)\r
4058     return -1;\r
4059   x -= lineGap;\r
4060   if ((x % (squareSize + lineGap)) >= squareSize)\r
4061     return -1;\r
4062   x /= (squareSize + lineGap);\r
4063     if (x >= limit)\r
4064     return -2;\r
4065   return x;\r
4066 }\r
4067 \r
4068 typedef struct {\r
4069   char piece;\r
4070   int command;\r
4071   char* name;\r
4072 } DropEnable;\r
4073 \r
4074 DropEnable dropEnables[] = {\r
4075   { 'P', DP_Pawn, N_("Pawn") },\r
4076   { 'N', DP_Knight, N_("Knight") },\r
4077   { 'B', DP_Bishop, N_("Bishop") },\r
4078   { 'R', DP_Rook, N_("Rook") },\r
4079   { 'Q', DP_Queen, N_("Queen") },\r
4080 };\r
4081 \r
4082 VOID\r
4083 SetupDropMenu(HMENU hmenu)\r
4084 {\r
4085   int i, count, enable;\r
4086   char *p;\r
4087   extern char white_holding[], black_holding[];\r
4088   char item[MSG_SIZ];\r
4089 \r
4090   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4091     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4092                dropEnables[i].piece);\r
4093     count = 0;\r
4094     while (p && *p++ == dropEnables[i].piece) count++;\r
4095       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4096     enable = count > 0 || !appData.testLegality\r
4097       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4098                       && !appData.icsActive);\r
4099     ModifyMenu(hmenu, dropEnables[i].command,\r
4100                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4101                dropEnables[i].command, item);\r
4102   }\r
4103 }\r
4104 \r
4105 void DragPieceBegin(int x, int y, Boolean instantly)\r
4106 {\r
4107       dragInfo.lastpos.x = boardRect.left + x;\r
4108       dragInfo.lastpos.y = boardRect.top + y;\r
4109       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4110       dragInfo.from.x = fromX;\r
4111       dragInfo.from.y = fromY;\r
4112       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4113       dragInfo.start = dragInfo.from;\r
4114       SetCapture(hwndMain);\r
4115 }\r
4116 \r
4117 void DragPieceEnd(int x, int y)\r
4118 {\r
4119     ReleaseCapture();\r
4120     dragInfo.start.x = dragInfo.start.y = -1;\r
4121     dragInfo.from = dragInfo.start;\r
4122     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4123 }\r
4124 \r
4125 void ChangeDragPiece(ChessSquare piece)\r
4126 {\r
4127     dragInfo.piece = piece;\r
4128 }\r
4129 \r
4130 /* Event handler for mouse messages */\r
4131 VOID\r
4132 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4133 {\r
4134   int x, y, menuNr;\r
4135   POINT pt;\r
4136   static int recursive = 0;\r
4137   HMENU hmenu;\r
4138   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4139 \r
4140   if (recursive) {\r
4141     if (message == WM_MBUTTONUP) {\r
4142       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4143          to the middle button: we simulate pressing the left button too!\r
4144          */\r
4145       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4146       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4147     }\r
4148     return;\r
4149   }\r
4150   recursive++;\r
4151   \r
4152   pt.x = LOWORD(lParam);\r
4153   pt.y = HIWORD(lParam);\r
4154   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4155   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4156   if (!flipView && y >= 0) {\r
4157     y = BOARD_HEIGHT - 1 - y;\r
4158   }\r
4159   if (flipView && x >= 0) {\r
4160     x = BOARD_WIDTH - 1 - x;\r
4161   }\r
4162 \r
4163   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4164 \r
4165   switch (message) {\r
4166   case WM_LBUTTONDOWN:\r
4167       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4168         ClockClick(flipClock); break;\r
4169       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4170         ClockClick(!flipClock); break;\r
4171       }\r
4172       dragInfo.start.x = dragInfo.start.y = -1;\r
4173       dragInfo.from = dragInfo.start;\r
4174     if(fromX == -1 && frozen) { // not sure where this is for\r
4175                 fromX = fromY = -1; \r
4176       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4177       break;\r
4178     }\r
4179       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4180       DrawPosition(TRUE, NULL);\r
4181     break;\r
4182 \r
4183   case WM_LBUTTONUP:\r
4184       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4185       DrawPosition(TRUE, NULL);\r
4186     break;\r
4187 \r
4188   case WM_MOUSEMOVE:\r
4189     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4190     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4191     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4192     if ((appData.animateDragging || appData.highlightDragging)\r
4193         && (wParam & MK_LBUTTON)\r
4194         && dragInfo.from.x >= 0) \r
4195     {\r
4196       BOOL full_repaint = FALSE;\r
4197 \r
4198       if (appData.animateDragging) {\r
4199         dragInfo.pos = pt;\r
4200       }\r
4201       if (appData.highlightDragging) {\r
4202         SetHighlights(fromX, fromY, x, y);\r
4203         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4204             full_repaint = TRUE;\r
4205         }\r
4206       }\r
4207       \r
4208       DrawPosition( full_repaint, NULL);\r
4209       \r
4210       dragInfo.lastpos = dragInfo.pos;\r
4211     }\r
4212     break;\r
4213 \r
4214   case WM_MOUSEWHEEL: // [DM]\r
4215     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4216        /* Mouse Wheel is being rolled forward\r
4217         * Play moves forward\r
4218         */\r
4219        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4220                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4221        /* Mouse Wheel is being rolled backward\r
4222         * Play moves backward\r
4223         */\r
4224        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4225                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4226     }\r
4227     break;\r
4228 \r
4229   case WM_MBUTTONUP:\r
4230   case WM_RBUTTONUP:\r
4231     ReleaseCapture();\r
4232     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4233     break;\r
4234  \r
4235   case WM_MBUTTONDOWN:\r
4236   case WM_RBUTTONDOWN:\r
4237     ErrorPopDown();\r
4238     ReleaseCapture();\r
4239     fromX = fromY = -1;\r
4240     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4241     dragInfo.start.x = dragInfo.start.y = -1;\r
4242     dragInfo.from = dragInfo.start;\r
4243     dragInfo.lastpos = dragInfo.pos;\r
4244     if (appData.highlightDragging) {\r
4245       ClearHighlights();\r
4246     }\r
4247     if(y == -2) {\r
4248       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4249       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4250           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4251       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4252           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4253       }\r
4254       break;\r
4255     }\r
4256     DrawPosition(TRUE, NULL);\r
4257 \r
4258     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4259     switch (menuNr) {\r
4260     case 0:\r
4261       if (message == WM_MBUTTONDOWN) {\r
4262         buttonCount = 3;  /* even if system didn't think so */\r
4263         if (wParam & MK_SHIFT) \r
4264           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4265         else\r
4266           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4267       } else { /* message == WM_RBUTTONDOWN */\r
4268         /* Just have one menu, on the right button.  Windows users don't\r
4269            think to try the middle one, and sometimes other software steals\r
4270            it, or it doesn't really exist. */\r
4271         if(gameInfo.variant != VariantShogi)\r
4272             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4273         else\r
4274             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4275       }\r
4276       break;\r
4277     case 2:\r
4278       SetCapture(hwndMain);\r
4279       break;\r
4280     case 1:\r
4281       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4282       SetupDropMenu(hmenu);\r
4283       MenuPopup(hwnd, pt, hmenu, -1);\r
4284     default:\r
4285       break;\r
4286     }\r
4287     break;\r
4288   }\r
4289 \r
4290   recursive--;\r
4291 }\r
4292 \r
4293 /* Preprocess messages for buttons in main window */\r
4294 LRESULT CALLBACK\r
4295 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4296 {\r
4297   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4298   int i, dir;\r
4299 \r
4300   for (i=0; i<N_BUTTONS; i++) {\r
4301     if (buttonDesc[i].id == id) break;\r
4302   }\r
4303   if (i == N_BUTTONS) return 0;\r
4304   switch (message) {\r
4305   case WM_KEYDOWN:\r
4306     switch (wParam) {\r
4307     case VK_LEFT:\r
4308     case VK_RIGHT:\r
4309       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4310       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4311       return TRUE;\r
4312     }\r
4313     break;\r
4314   case WM_CHAR:\r
4315     switch (wParam) {\r
4316     case '\r':\r
4317       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4318       return TRUE;\r
4319     default:\r
4320       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4321         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4322         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4323         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4324         SetFocus(h);\r
4325         SendMessage(h, WM_CHAR, wParam, lParam);\r
4326         return TRUE;\r
4327       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4328         TypeInEvent((char)wParam);\r
4329       }\r
4330       break;\r
4331     }\r
4332     break;\r
4333   }\r
4334   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4335 }\r
4336 \r
4337 /* Process messages for Promotion dialog box */\r
4338 LRESULT CALLBACK\r
4339 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4340 {\r
4341   char promoChar;\r
4342 \r
4343   switch (message) {\r
4344   case WM_INITDIALOG: /* message: initialize dialog box */\r
4345     /* Center the dialog over the application window */\r
4346     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4347     Translate(hDlg, DLG_PromotionKing);\r
4348     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4349       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4350        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4351        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4352                SW_SHOW : SW_HIDE);\r
4353     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4354     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4355        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4356          PieceToChar(WhiteAngel) != '~') ||\r
4357         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4358          PieceToChar(BlackAngel) != '~')   ) ?\r
4359                SW_SHOW : SW_HIDE);\r
4360     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4361        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4362          PieceToChar(WhiteMarshall) != '~') ||\r
4363         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4364          PieceToChar(BlackMarshall) != '~')   ) ?\r
4365                SW_SHOW : SW_HIDE);\r
4366     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4367     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4368        gameInfo.variant != VariantShogi ?\r
4369                SW_SHOW : SW_HIDE);\r
4370     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4371        gameInfo.variant != VariantShogi ?\r
4372                SW_SHOW : SW_HIDE);\r
4373     if(gameInfo.variant == VariantShogi) {\r
4374         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4375         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4376         SetWindowText(hDlg, "Promote?");\r
4377     }\r
4378     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4379        gameInfo.variant == VariantSuper ?\r
4380                SW_SHOW : SW_HIDE);\r
4381     return TRUE;\r
4382 \r
4383   case WM_COMMAND: /* message: received a command */\r
4384     switch (LOWORD(wParam)) {\r
4385     case IDCANCEL:\r
4386       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4387       ClearHighlights();\r
4388       DrawPosition(FALSE, NULL);\r
4389       return TRUE;\r
4390     case PB_King:\r
4391       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4392       break;\r
4393     case PB_Queen:\r
4394       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4395       break;\r
4396     case PB_Rook:\r
4397       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4398       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4399       break;\r
4400     case PB_Bishop:\r
4401       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4402       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4403       break;\r
4404     case PB_Chancellor:\r
4405       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4406       break;\r
4407     case PB_Archbishop:\r
4408       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4409       break;\r
4410     case PB_Knight:\r
4411       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4412       break;\r
4413     default:\r
4414       return FALSE;\r
4415     }\r
4416     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4417     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4418     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4419     fromX = fromY = -1;\r
4420     if (!appData.highlightLastMove) {\r
4421       ClearHighlights();\r
4422       DrawPosition(FALSE, NULL);\r
4423     }\r
4424     return TRUE;\r
4425   }\r
4426   return FALSE;\r
4427 }\r
4428 \r
4429 /* Pop up promotion dialog */\r
4430 VOID\r
4431 PromotionPopup(HWND hwnd)\r
4432 {\r
4433   FARPROC lpProc;\r
4434 \r
4435   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4436   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4437     hwnd, (DLGPROC)lpProc);\r
4438   FreeProcInstance(lpProc);\r
4439 }\r
4440 \r
4441 void\r
4442 PromotionPopUp()\r
4443 {\r
4444   DrawPosition(TRUE, NULL);\r
4445   PromotionPopup(hwndMain);\r
4446 }\r
4447 \r
4448 /* Toggle ShowThinking */\r
4449 VOID\r
4450 ToggleShowThinking()\r
4451 {\r
4452   appData.showThinking = !appData.showThinking;\r
4453   ShowThinkingEvent();\r
4454 }\r
4455 \r
4456 VOID\r
4457 LoadGameDialog(HWND hwnd, char* title)\r
4458 {\r
4459   UINT number = 0;\r
4460   FILE *f;\r
4461   char fileTitle[MSG_SIZ];\r
4462   f = OpenFileDialog(hwnd, "rb", "",\r
4463                      appData.oldSaveStyle ? "gam" : "pgn",\r
4464                      GAME_FILT,\r
4465                      title, &number, fileTitle, NULL);\r
4466   if (f != NULL) {\r
4467     cmailMsgLoaded = FALSE;\r
4468     if (number == 0) {\r
4469       int error = GameListBuild(f);\r
4470       if (error) {\r
4471         DisplayError(_("Cannot build game list"), error);\r
4472       } else if (!ListEmpty(&gameList) &&\r
4473                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4474         GameListPopUp(f, fileTitle);\r
4475         return;\r
4476       }\r
4477       GameListDestroy();\r
4478       number = 1;\r
4479     }\r
4480     LoadGame(f, number, fileTitle, FALSE);\r
4481   }\r
4482 }\r
4483 \r
4484 int get_term_width()\r
4485 {\r
4486     HDC hdc;\r
4487     TEXTMETRIC tm;\r
4488     RECT rc;\r
4489     HFONT hfont, hold_font;\r
4490     LOGFONT lf;\r
4491     HWND hText;\r
4492 \r
4493     if (hwndConsole)\r
4494         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4495     else\r
4496         return 79;\r
4497 \r
4498     // get the text metrics\r
4499     hdc = GetDC(hText);\r
4500     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4501     if (consoleCF.dwEffects & CFE_BOLD)\r
4502         lf.lfWeight = FW_BOLD;\r
4503     if (consoleCF.dwEffects & CFE_ITALIC)\r
4504         lf.lfItalic = TRUE;\r
4505     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4506         lf.lfStrikeOut = TRUE;\r
4507     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4508         lf.lfUnderline = TRUE;\r
4509     hfont = CreateFontIndirect(&lf);\r
4510     hold_font = SelectObject(hdc, hfont);\r
4511     GetTextMetrics(hdc, &tm);\r
4512     SelectObject(hdc, hold_font);\r
4513     DeleteObject(hfont);\r
4514     ReleaseDC(hText, hdc);\r
4515 \r
4516     // get the rectangle\r
4517     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4518 \r
4519     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4520 }\r
4521 \r
4522 void UpdateICSWidth(HWND hText)\r
4523 {\r
4524     LONG old_width, new_width;\r
4525 \r
4526     new_width = get_term_width(hText, FALSE);\r
4527     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4528     if (new_width != old_width)\r
4529     {\r
4530         ics_update_width(new_width);\r
4531         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4532     }\r
4533 }\r
4534 \r
4535 VOID\r
4536 ChangedConsoleFont()\r
4537 {\r
4538   CHARFORMAT cfmt;\r
4539   CHARRANGE tmpsel, sel;\r
4540   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4541   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4542   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4543   PARAFORMAT paraf;\r
4544 \r
4545   cfmt.cbSize = sizeof(CHARFORMAT);\r
4546   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4547     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4548                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4549   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4550    * size.  This was undocumented in the version of MSVC++ that I had\r
4551    * when I wrote the code, but is apparently documented now.\r
4552    */\r
4553   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4554   cfmt.bCharSet = f->lf.lfCharSet;\r
4555   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4556   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4557   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4558   /* Why are the following seemingly needed too? */\r
4559   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4560   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4561   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4562   tmpsel.cpMin = 0;\r
4563   tmpsel.cpMax = -1; /*999999?*/\r
4564   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4565   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4566   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4567    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4568    */\r
4569   paraf.cbSize = sizeof(paraf);\r
4570   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4571   paraf.dxStartIndent = 0;\r
4572   paraf.dxOffset = WRAP_INDENT;\r
4573   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4574   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4575   UpdateICSWidth(hText);\r
4576 }\r
4577 \r
4578 /*---------------------------------------------------------------------------*\\r
4579  *\r
4580  * Window Proc for main window\r
4581  *\r
4582 \*---------------------------------------------------------------------------*/\r
4583 \r
4584 /* Process messages for main window, etc. */\r
4585 LRESULT CALLBACK\r
4586 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4587 {\r
4588   FARPROC lpProc;\r
4589   int wmId, wmEvent;\r
4590   char *defName;\r
4591   FILE *f;\r
4592   UINT number;\r
4593   char fileTitle[MSG_SIZ];\r
4594   char buf[MSG_SIZ];\r
4595   static SnapData sd;\r
4596   static int peek=0;\r
4597 \r
4598   switch (message) {\r
4599 \r
4600   case WM_PAINT: /* message: repaint portion of window */\r
4601     PaintProc(hwnd);\r
4602     break;\r
4603 \r
4604   case WM_ERASEBKGND:\r
4605     if (IsIconic(hwnd)) {\r
4606       /* Cheat; change the message */\r
4607       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4608     } else {\r
4609       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4610     }\r
4611     break;\r
4612 \r
4613   case WM_LBUTTONDOWN:\r
4614   case WM_MBUTTONDOWN:\r
4615   case WM_RBUTTONDOWN:\r
4616   case WM_LBUTTONUP:\r
4617   case WM_MBUTTONUP:\r
4618   case WM_RBUTTONUP:\r
4619   case WM_MOUSEMOVE:\r
4620   case WM_MOUSEWHEEL:\r
4621     MouseEvent(hwnd, message, wParam, lParam);\r
4622     break;\r
4623 \r
4624   case WM_KEYUP:\r
4625     if((char)wParam == '\b') {\r
4626       ForwardEvent(); peek = 0;\r
4627     }\r
4628 \r
4629     JAWS_KBUP_NAVIGATION\r
4630 \r
4631     break;\r
4632 \r
4633   case WM_KEYDOWN:\r
4634     if((char)wParam == '\b') {\r
4635       if(!peek) BackwardEvent(), peek = 1;\r
4636     }\r
4637 \r
4638     JAWS_KBDOWN_NAVIGATION\r
4639 \r
4640     break;\r
4641 \r
4642   case WM_CHAR:\r
4643     \r
4644     JAWS_ALT_INTERCEPT\r
4645 \r
4646     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4647         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4648         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4649         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4650         SetFocus(h);\r
4651         SendMessage(h, message, wParam, lParam);\r
4652     } else if(lParam != KF_REPEAT) {\r
4653         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4654                 TypeInEvent((char)wParam);\r
4655         } else if((char)wParam == 003) CopyGameToClipboard();\r
4656          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4657     }\r
4658 \r
4659     break;\r
4660 \r
4661   case WM_PALETTECHANGED:\r
4662     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4663       int nnew;\r
4664       HDC hdc = GetDC(hwndMain);\r
4665       SelectPalette(hdc, hPal, TRUE);\r
4666       nnew = RealizePalette(hdc);\r
4667       if (nnew > 0) {\r
4668         paletteChanged = TRUE;\r
4669         InvalidateRect(hwnd, &boardRect, FALSE);\r
4670       }\r
4671       ReleaseDC(hwnd, hdc);\r
4672     }\r
4673     break;\r
4674 \r
4675   case WM_QUERYNEWPALETTE:\r
4676     if (!appData.monoMode /*&& paletteChanged*/) {\r
4677       int nnew;\r
4678       HDC hdc = GetDC(hwndMain);\r
4679       paletteChanged = FALSE;\r
4680       SelectPalette(hdc, hPal, FALSE);\r
4681       nnew = RealizePalette(hdc);\r
4682       if (nnew > 0) {\r
4683         InvalidateRect(hwnd, &boardRect, FALSE);\r
4684       }\r
4685       ReleaseDC(hwnd, hdc);\r
4686       return TRUE;\r
4687     }\r
4688     return FALSE;\r
4689 \r
4690   case WM_COMMAND: /* message: command from application menu */\r
4691     wmId    = LOWORD(wParam);\r
4692     wmEvent = HIWORD(wParam);\r
4693 \r
4694     switch (wmId) {\r
4695     case IDM_NewGame:\r
4696       ResetGameEvent();\r
4697       SAY("new game enter a move to play against the computer with white");\r
4698       break;\r
4699 \r
4700     case IDM_NewGameFRC:\r
4701       if( NewGameFRC() == 0 ) {\r
4702         ResetGameEvent();\r
4703       }\r
4704       break;\r
4705 \r
4706     case IDM_NewVariant:\r
4707       NewVariantPopup(hwnd);\r
4708       break;\r
4709 \r
4710     case IDM_LoadGame:\r
4711       LoadGameDialog(hwnd, _("Load Game from File"));\r
4712       break;\r
4713 \r
4714     case IDM_LoadNextGame:\r
4715       ReloadGame(1);\r
4716       break;\r
4717 \r
4718     case IDM_LoadPrevGame:\r
4719       ReloadGame(-1);\r
4720       break;\r
4721 \r
4722     case IDM_ReloadGame:\r
4723       ReloadGame(0);\r
4724       break;\r
4725 \r
4726     case IDM_LoadPosition:\r
4727       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4728         Reset(FALSE, TRUE);\r
4729       }\r
4730       number = 1;\r
4731       f = OpenFileDialog(hwnd, "rb", "",\r
4732                          appData.oldSaveStyle ? "pos" : "fen",\r
4733                          POSITION_FILT,\r
4734                          _("Load Position from File"), &number, fileTitle, NULL);\r
4735       if (f != NULL) {\r
4736         LoadPosition(f, number, fileTitle);\r
4737       }\r
4738       break;\r
4739 \r
4740     case IDM_LoadNextPosition:\r
4741       ReloadPosition(1);\r
4742       break;\r
4743 \r
4744     case IDM_LoadPrevPosition:\r
4745       ReloadPosition(-1);\r
4746       break;\r
4747 \r
4748     case IDM_ReloadPosition:\r
4749       ReloadPosition(0);\r
4750       break;\r
4751 \r
4752     case IDM_SaveGame:\r
4753       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4754       f = OpenFileDialog(hwnd, "a", defName,\r
4755                          appData.oldSaveStyle ? "gam" : "pgn",\r
4756                          GAME_FILT,\r
4757                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4758       if (f != NULL) {\r
4759         SaveGame(f, 0, "");\r
4760       }\r
4761       break;\r
4762 \r
4763     case IDM_SavePosition:\r
4764       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4765       f = OpenFileDialog(hwnd, "a", defName,\r
4766                          appData.oldSaveStyle ? "pos" : "fen",\r
4767                          POSITION_FILT,\r
4768                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4769       if (f != NULL) {\r
4770         SavePosition(f, 0, "");\r
4771       }\r
4772       break;\r
4773 \r
4774     case IDM_SaveDiagram:\r
4775       defName = "diagram";\r
4776       f = OpenFileDialog(hwnd, "wb", defName,\r
4777                          "bmp",\r
4778                          DIAGRAM_FILT,\r
4779                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4780       if (f != NULL) {\r
4781         SaveDiagram(f);\r
4782       }\r
4783       break;\r
4784 \r
4785     case IDM_CopyGame:\r
4786       CopyGameToClipboard();\r
4787       break;\r
4788 \r
4789     case IDM_PasteGame:\r
4790       PasteGameFromClipboard();\r
4791       break;\r
4792 \r
4793     case IDM_CopyGameListToClipboard:\r
4794       CopyGameListToClipboard();\r
4795       break;\r
4796 \r
4797     /* [AS] Autodetect FEN or PGN data */\r
4798     case IDM_PasteAny:\r
4799       PasteGameOrFENFromClipboard();\r
4800       break;\r
4801 \r
4802     /* [AS] Move history */\r
4803     case IDM_ShowMoveHistory:\r
4804         if( MoveHistoryIsUp() ) {\r
4805             MoveHistoryPopDown();\r
4806         }\r
4807         else {\r
4808             MoveHistoryPopUp();\r
4809         }\r
4810         break;\r
4811 \r
4812     /* [AS] Eval graph */\r
4813     case IDM_ShowEvalGraph:\r
4814         if( EvalGraphIsUp() ) {\r
4815             EvalGraphPopDown();\r
4816         }\r
4817         else {\r
4818             EvalGraphPopUp();\r
4819             SetFocus(hwndMain);\r
4820         }\r
4821         break;\r
4822 \r
4823     /* [AS] Engine output */\r
4824     case IDM_ShowEngineOutput:\r
4825         if( EngineOutputIsUp() ) {\r
4826             EngineOutputPopDown();\r
4827         }\r
4828         else {\r
4829             EngineOutputPopUp();\r
4830         }\r
4831         break;\r
4832 \r
4833     /* [AS] User adjudication */\r
4834     case IDM_UserAdjudication_White:\r
4835         UserAdjudicationEvent( +1 );\r
4836         break;\r
4837 \r
4838     case IDM_UserAdjudication_Black:\r
4839         UserAdjudicationEvent( -1 );\r
4840         break;\r
4841 \r
4842     case IDM_UserAdjudication_Draw:\r
4843         UserAdjudicationEvent( 0 );\r
4844         break;\r
4845 \r
4846     /* [AS] Game list options dialog */\r
4847     case IDM_GameListOptions:\r
4848       GameListOptions();\r
4849       break;\r
4850 \r
4851     case IDM_NewChat:\r
4852       ChatPopUp(NULL);\r
4853       break;\r
4854 \r
4855     case IDM_CopyPosition:\r
4856       CopyFENToClipboard();\r
4857       break;\r
4858 \r
4859     case IDM_PastePosition:\r
4860       PasteFENFromClipboard();\r
4861       break;\r
4862 \r
4863     case IDM_MailMove:\r
4864       MailMoveEvent();\r
4865       break;\r
4866 \r
4867     case IDM_ReloadCMailMsg:\r
4868       Reset(TRUE, TRUE);\r
4869       ReloadCmailMsgEvent(FALSE);\r
4870       break;\r
4871 \r
4872     case IDM_Minimize:\r
4873       ShowWindow(hwnd, SW_MINIMIZE);\r
4874       break;\r
4875 \r
4876     case IDM_Exit:\r
4877       ExitEvent(0);\r
4878       break;\r
4879 \r
4880     case IDM_MachineWhite:\r
4881       MachineWhiteEvent();\r
4882       /*\r
4883        * refresh the tags dialog only if it's visible\r
4884        */\r
4885       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4886           char *tags;\r
4887           tags = PGNTags(&gameInfo);\r
4888           TagsPopUp(tags, CmailMsg());\r
4889           free(tags);\r
4890       }\r
4891       SAY("computer starts playing white");\r
4892       break;\r
4893 \r
4894     case IDM_MachineBlack:\r
4895       MachineBlackEvent();\r
4896       /*\r
4897        * refresh the tags dialog only if it's visible\r
4898        */\r
4899       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4900           char *tags;\r
4901           tags = PGNTags(&gameInfo);\r
4902           TagsPopUp(tags, CmailMsg());\r
4903           free(tags);\r
4904       }\r
4905       SAY("computer starts playing black");\r
4906       break;\r
4907 \r
4908     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4909       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4910       break;\r
4911 \r
4912     case IDM_TwoMachines:\r
4913       TwoMachinesEvent();\r
4914       /*\r
4915        * refresh the tags dialog only if it's visible\r
4916        */\r
4917       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4918           char *tags;\r
4919           tags = PGNTags(&gameInfo);\r
4920           TagsPopUp(tags, CmailMsg());\r
4921           free(tags);\r
4922       }\r
4923       SAY("computer starts playing both sides");\r
4924       break;\r
4925 \r
4926     case IDM_AnalysisMode:\r
4927       if (!first.analysisSupport) {\r
4928         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4929         DisplayError(buf, 0);\r
4930       } else {\r
4931         SAY("analyzing current position");\r
4932         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4933         if (appData.icsActive) {\r
4934                if (gameMode != IcsObserving) {\r
4935                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4936                        DisplayError(buf, 0);\r
4937                        /* secure check */\r
4938                        if (appData.icsEngineAnalyze) {\r
4939                                if (appData.debugMode) \r
4940                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4941                                ExitAnalyzeMode();\r
4942                                ModeHighlight();\r
4943                                break;\r
4944                        }\r
4945                        break;\r
4946                } else {\r
4947                        /* if enable, user want disable icsEngineAnalyze */\r
4948                        if (appData.icsEngineAnalyze) {\r
4949                                ExitAnalyzeMode();\r
4950                                ModeHighlight();\r
4951                                break;\r
4952                        }\r
4953                        appData.icsEngineAnalyze = TRUE;\r
4954                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4955                }\r
4956         } \r
4957         if (!appData.showThinking) ToggleShowThinking();\r
4958         AnalyzeModeEvent();\r
4959       }\r
4960       break;\r
4961 \r
4962     case IDM_AnalyzeFile:\r
4963       if (!first.analysisSupport) {\r
4964         char buf[MSG_SIZ];\r
4965           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4966         DisplayError(buf, 0);\r
4967       } else {\r
4968         if (!appData.showThinking) ToggleShowThinking();\r
4969         AnalyzeFileEvent();\r
4970 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4971         AnalysisPeriodicEvent(1);\r
4972       }\r
4973       break;\r
4974 \r
4975     case IDM_IcsClient:\r
4976       IcsClientEvent();\r
4977       break;\r
4978 \r
4979     case IDM_EditGame:\r
4980     case IDM_EditGame2:\r
4981       EditGameEvent();\r
4982       SAY("edit game");\r
4983       break;\r
4984 \r
4985     case IDM_EditPosition:\r
4986     case IDM_EditPosition2:\r
4987       EditPositionEvent();\r
4988       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4989       break;\r
4990 \r
4991     case IDM_Training:\r
4992       TrainingEvent();\r
4993       break;\r
4994 \r
4995     case IDM_ShowGameList:\r
4996       ShowGameListProc();\r
4997       break;\r
4998 \r
4999     case IDM_EditProgs1:\r
5000       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5001       break;\r
5002 \r
5003     case IDM_LoadProg1:\r
5004      LoadEnginePopUp(hwndMain, 0);\r
5005       break;\r
5006 \r
5007     case IDM_LoadProg2:\r
5008      LoadEnginePopUp(hwndMain, 1);\r
5009       break;\r
5010 \r
5011     case IDM_EditServers:\r
5012       EditTagsPopUp(icsNames, &icsNames);\r
5013       break;\r
5014 \r
5015     case IDM_EditTags:\r
5016     case IDM_Tags:\r
5017       EditTagsProc();\r
5018       break;\r
5019 \r
5020     case IDM_EditBook:\r
5021       EditBookEvent();\r
5022       break;\r
5023 \r
5024     case IDM_EditComment:\r
5025     case IDM_Comment:\r
5026       if (commentUp && editComment) {\r
5027         CommentPopDown();\r
5028       } else {\r
5029         EditCommentEvent();\r
5030       }\r
5031       break;\r
5032 \r
5033     case IDM_Pause:\r
5034       PauseEvent();\r
5035       break;\r
5036 \r
5037     case IDM_Accept:\r
5038       AcceptEvent();\r
5039       break;\r
5040 \r
5041     case IDM_Decline:\r
5042       DeclineEvent();\r
5043       break;\r
5044 \r
5045     case IDM_Rematch:\r
5046       RematchEvent();\r
5047       break;\r
5048 \r
5049     case IDM_CallFlag:\r
5050       CallFlagEvent();\r
5051       break;\r
5052 \r
5053     case IDM_Draw:\r
5054       DrawEvent();\r
5055       break;\r
5056 \r
5057     case IDM_Adjourn:\r
5058       AdjournEvent();\r
5059       break;\r
5060 \r
5061     case IDM_Abort:\r
5062       AbortEvent();\r
5063       break;\r
5064 \r
5065     case IDM_Resign:\r
5066       ResignEvent();\r
5067       break;\r
5068 \r
5069     case IDM_StopObserving:\r
5070       StopObservingEvent();\r
5071       break;\r
5072 \r
5073     case IDM_StopExamining:\r
5074       StopExaminingEvent();\r
5075       break;\r
5076 \r
5077     case IDM_Upload:\r
5078       UploadGameEvent();\r
5079       break;\r
5080 \r
5081     case IDM_TypeInMove:\r
5082       TypeInEvent('\000');\r
5083       break;\r
5084 \r
5085     case IDM_TypeInName:\r
5086       PopUpNameDialog('\000');\r
5087       break;\r
5088 \r
5089     case IDM_Backward:\r
5090       BackwardEvent();\r
5091       SetFocus(hwndMain);\r
5092       break;\r
5093 \r
5094     JAWS_MENU_ITEMS\r
5095 \r
5096     case IDM_Forward:\r
5097       ForwardEvent();\r
5098       SetFocus(hwndMain);\r
5099       break;\r
5100 \r
5101     case IDM_ToStart:\r
5102       ToStartEvent();\r
5103       SetFocus(hwndMain);\r
5104       break;\r
5105 \r
5106     case IDM_ToEnd:\r
5107       ToEndEvent();\r
5108       SetFocus(hwndMain);\r
5109       break;\r
5110 \r
5111     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5112     case OPT_GameListPrev:\r
5113       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5114       break;\r
5115 \r
5116     case IDM_Revert:\r
5117       RevertEvent(FALSE);\r
5118       break;\r
5119 \r
5120     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5121       RevertEvent(TRUE);\r
5122       break;\r
5123 \r
5124     case IDM_TruncateGame:\r
5125       TruncateGameEvent();\r
5126       break;\r
5127 \r
5128     case IDM_MoveNow:\r
5129       MoveNowEvent();\r
5130       break;\r
5131 \r
5132     case IDM_RetractMove:\r
5133       RetractMoveEvent();\r
5134       break;\r
5135 \r
5136     case IDM_FlipView:\r
5137       flipView = !flipView;\r
5138       DrawPosition(FALSE, NULL);\r
5139       break;\r
5140 \r
5141     case IDM_FlipClock:\r
5142       flipClock = !flipClock;\r
5143       DisplayBothClocks();\r
5144       DisplayLogos();\r
5145       break;\r
5146 \r
5147     case IDM_MuteSounds:\r
5148       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5149       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5150                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5151       break;\r
5152 \r
5153     case IDM_GeneralOptions:\r
5154       GeneralOptionsPopup(hwnd);\r
5155       DrawPosition(TRUE, NULL);\r
5156       break;\r
5157 \r
5158     case IDM_BoardOptions:\r
5159       BoardOptionsPopup(hwnd);\r
5160       break;\r
5161 \r
5162     case IDM_EnginePlayOptions:\r
5163       EnginePlayOptionsPopup(hwnd);\r
5164       break;\r
5165 \r
5166     case IDM_Engine1Options:\r
5167       EngineOptionsPopup(hwnd, &first);\r
5168       break;\r
5169 \r
5170     case IDM_Engine2Options:\r
5171       savedHwnd = hwnd;\r
5172       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5173       EngineOptionsPopup(hwnd, &second);\r
5174       break;\r
5175 \r
5176     case IDM_OptionsUCI:\r
5177       UciOptionsPopup(hwnd);\r
5178       break;\r
5179 \r
5180     case IDM_Tourney:\r
5181       TourneyPopup(hwnd);\r
5182       break;\r
5183 \r
5184     case IDM_IcsOptions:\r
5185       IcsOptionsPopup(hwnd);\r
5186       break;\r
5187 \r
5188     case IDM_Fonts:\r
5189       FontsOptionsPopup(hwnd);\r
5190       break;\r
5191 \r
5192     case IDM_Sounds:\r
5193       SoundOptionsPopup(hwnd);\r
5194       break;\r
5195 \r
5196     case IDM_CommPort:\r
5197       CommPortOptionsPopup(hwnd);\r
5198       break;\r
5199 \r
5200     case IDM_LoadOptions:\r
5201       LoadOptionsPopup(hwnd);\r
5202       break;\r
5203 \r
5204     case IDM_SaveOptions:\r
5205       SaveOptionsPopup(hwnd);\r
5206       break;\r
5207 \r
5208     case IDM_TimeControl:\r
5209       TimeControlOptionsPopup(hwnd);\r
5210       break;\r
5211 \r
5212     case IDM_SaveSettings:\r
5213       SaveSettings(settingsFileName);\r
5214       break;\r
5215 \r
5216     case IDM_SaveSettingsOnExit:\r
5217       saveSettingsOnExit = !saveSettingsOnExit;\r
5218       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5219                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5220                                          MF_CHECKED : MF_UNCHECKED));\r
5221       break;\r
5222 \r
5223     case IDM_Hint:\r
5224       HintEvent();\r
5225       break;\r
5226 \r
5227     case IDM_Book:\r
5228       BookEvent();\r
5229       break;\r
5230 \r
5231     case IDM_AboutGame:\r
5232       AboutGameEvent();\r
5233       break;\r
5234 \r
5235     case IDM_Debug:\r
5236       appData.debugMode = !appData.debugMode;\r
5237       if (appData.debugMode) {\r
5238         char dir[MSG_SIZ];\r
5239         GetCurrentDirectory(MSG_SIZ, dir);\r
5240         SetCurrentDirectory(installDir);\r
5241         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5242         SetCurrentDirectory(dir);\r
5243         setbuf(debugFP, NULL);\r
5244       } else {\r
5245         fclose(debugFP);\r
5246         debugFP = NULL;\r
5247       }\r
5248       break;\r
5249 \r
5250     case IDM_HELPCONTENTS:\r
5251       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5252           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5253           MessageBox (GetFocus(),\r
5254                     _("Unable to activate help"),\r
5255                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5256       }\r
5257       break;\r
5258 \r
5259     case IDM_HELPSEARCH:\r
5260         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5261             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5262         MessageBox (GetFocus(),\r
5263                     _("Unable to activate help"),\r
5264                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5265       }\r
5266       break;\r
5267 \r
5268     case IDM_HELPHELP:\r
5269       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5270         MessageBox (GetFocus(),\r
5271                     _("Unable to activate help"),\r
5272                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5273       }\r
5274       break;\r
5275 \r
5276     case IDM_ABOUT:\r
5277       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5278       DialogBox(hInst, \r
5279         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5280         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5281       FreeProcInstance(lpProc);\r
5282       break;\r
5283 \r
5284     case IDM_DirectCommand1:\r
5285       AskQuestionEvent(_("Direct Command"),\r
5286                        _("Send to chess program:"), "", "1");\r
5287       break;\r
5288     case IDM_DirectCommand2:\r
5289       AskQuestionEvent(_("Direct Command"),\r
5290                        _("Send to second chess program:"), "", "2");\r
5291       break;\r
5292 \r
5293     case EP_WhitePawn:\r
5294       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_WhiteKnight:\r
5299       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_WhiteBishop:\r
5304       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_WhiteRook:\r
5309       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_WhiteQueen:\r
5314       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_WhiteFerz:\r
5319       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_WhiteWazir:\r
5324       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_WhiteAlfil:\r
5329       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_WhiteCannon:\r
5334       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_WhiteCardinal:\r
5339       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_WhiteMarshall:\r
5344       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_WhiteKing:\r
5349       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_BlackPawn:\r
5354       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_BlackKnight:\r
5359       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_BlackBishop:\r
5364       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_BlackRook:\r
5369       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_BlackQueen:\r
5374       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_BlackFerz:\r
5379       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case EP_BlackWazir:\r
5384       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case EP_BlackAlfil:\r
5389       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case EP_BlackCannon:\r
5394       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case EP_BlackCardinal:\r
5399       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case EP_BlackMarshall:\r
5404       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case EP_BlackKing:\r
5409       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5410       fromX = fromY = -1;\r
5411       break;\r
5412 \r
5413     case EP_EmptySquare:\r
5414       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5415       fromX = fromY = -1;\r
5416       break;\r
5417 \r
5418     case EP_ClearBoard:\r
5419       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5420       fromX = fromY = -1;\r
5421       break;\r
5422 \r
5423     case EP_White:\r
5424       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5425       fromX = fromY = -1;\r
5426       break;\r
5427 \r
5428     case EP_Black:\r
5429       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5430       fromX = fromY = -1;\r
5431       break;\r
5432 \r
5433     case EP_Promote:\r
5434       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5435       fromX = fromY = -1;\r
5436       break;\r
5437 \r
5438     case EP_Demote:\r
5439       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5440       fromX = fromY = -1;\r
5441       break;\r
5442 \r
5443     case DP_Pawn:\r
5444       DropMenuEvent(WhitePawn, fromX, fromY);\r
5445       fromX = fromY = -1;\r
5446       break;\r
5447 \r
5448     case DP_Knight:\r
5449       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5450       fromX = fromY = -1;\r
5451       break;\r
5452 \r
5453     case DP_Bishop:\r
5454       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5455       fromX = fromY = -1;\r
5456       break;\r
5457 \r
5458     case DP_Rook:\r
5459       DropMenuEvent(WhiteRook, fromX, fromY);\r
5460       fromX = fromY = -1;\r
5461       break;\r
5462 \r
5463     case DP_Queen:\r
5464       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5465       fromX = fromY = -1;\r
5466       break;\r
5467 \r
5468     case IDM_English:\r
5469       barbaric = 0; appData.language = "";\r
5470       TranslateMenus(0);\r
5471       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5472       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5473       lastChecked = wmId;\r
5474       break;\r
5475 \r
5476     default:\r
5477       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5478           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5479       else\r
5480       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5481           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5482           TranslateMenus(0);\r
5483           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5484           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5485           lastChecked = wmId;\r
5486           break;\r
5487       }\r
5488       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5489     }\r
5490     break;\r
5491 \r
5492   case WM_TIMER:\r
5493     switch (wParam) {\r
5494     case CLOCK_TIMER_ID:\r
5495       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5496       clockTimerEvent = 0;\r
5497       DecrementClocks(); /* call into back end */\r
5498       break;\r
5499     case LOAD_GAME_TIMER_ID:\r
5500       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5501       loadGameTimerEvent = 0;\r
5502       AutoPlayGameLoop(); /* call into back end */\r
5503       break;\r
5504     case ANALYSIS_TIMER_ID:\r
5505       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5506                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5507         AnalysisPeriodicEvent(0);\r
5508       } else {\r
5509         KillTimer(hwnd, analysisTimerEvent);\r
5510         analysisTimerEvent = 0;\r
5511       }\r
5512       break;\r
5513     case DELAYED_TIMER_ID:\r
5514       KillTimer(hwnd, delayedTimerEvent);\r
5515       delayedTimerEvent = 0;\r
5516       delayedTimerCallback();\r
5517       break;\r
5518     }\r
5519     break;\r
5520 \r
5521   case WM_USER_Input:\r
5522     InputEvent(hwnd, message, wParam, lParam);\r
5523     break;\r
5524 \r
5525   /* [AS] Also move "attached" child windows */\r
5526   case WM_WINDOWPOSCHANGING:\r
5527 \r
5528     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5529         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5530 \r
5531         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5532             /* Window is moving */\r
5533             RECT rcMain;\r
5534 \r
5535 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5536             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5537             rcMain.right  = wpMain.x + wpMain.width;\r
5538             rcMain.top    = wpMain.y;\r
5539             rcMain.bottom = wpMain.y + wpMain.height;\r
5540             \r
5541             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5542             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5543             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5544             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5545             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5546             wpMain.x = lpwp->x;\r
5547             wpMain.y = lpwp->y;\r
5548         }\r
5549     }\r
5550     break;\r
5551 \r
5552   /* [AS] Snapping */\r
5553   case WM_ENTERSIZEMOVE:\r
5554     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5555     if (hwnd == hwndMain) {\r
5556       doingSizing = TRUE;\r
5557       lastSizing = 0;\r
5558     }\r
5559     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5560     break;\r
5561 \r
5562   case WM_SIZING:\r
5563     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5564     if (hwnd == hwndMain) {\r
5565       lastSizing = wParam;\r
5566     }\r
5567     break;\r
5568 \r
5569   case WM_MOVING:\r
5570     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5571       return OnMoving( &sd, hwnd, wParam, lParam );\r
5572 \r
5573   case WM_EXITSIZEMOVE:\r
5574     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5575     if (hwnd == hwndMain) {\r
5576       RECT client;\r
5577       doingSizing = FALSE;\r
5578       InvalidateRect(hwnd, &boardRect, FALSE);\r
5579       GetClientRect(hwnd, &client);\r
5580       ResizeBoard(client.right, client.bottom, lastSizing);\r
5581       lastSizing = 0;\r
5582       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5583     }\r
5584     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5585     break;\r
5586 \r
5587   case WM_DESTROY: /* message: window being destroyed */\r
5588     PostQuitMessage(0);\r
5589     break;\r
5590 \r
5591   case WM_CLOSE:\r
5592     if (hwnd == hwndMain) {\r
5593       ExitEvent(0);\r
5594     }\r
5595     break;\r
5596 \r
5597   default:      /* Passes it on if unprocessed */\r
5598     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5599   }\r
5600   return 0;\r
5601 }\r
5602 \r
5603 /*---------------------------------------------------------------------------*\\r
5604  *\r
5605  * Misc utility routines\r
5606  *\r
5607 \*---------------------------------------------------------------------------*/\r
5608 \r
5609 /*\r
5610  * Decent random number generator, at least not as bad as Windows\r
5611  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5612  */\r
5613 unsigned int randstate;\r
5614 \r
5615 int\r
5616 myrandom(void)\r
5617 {\r
5618   randstate = randstate * 1664525 + 1013904223;\r
5619   return (int) randstate & 0x7fffffff;\r
5620 }\r
5621 \r
5622 void\r
5623 mysrandom(unsigned int seed)\r
5624 {\r
5625   randstate = seed;\r
5626 }\r
5627 \r
5628 \r
5629 /* \r
5630  * returns TRUE if user selects a different color, FALSE otherwise \r
5631  */\r
5632 \r
5633 BOOL\r
5634 ChangeColor(HWND hwnd, COLORREF *which)\r
5635 {\r
5636   static BOOL firstTime = TRUE;\r
5637   static DWORD customColors[16];\r
5638   CHOOSECOLOR cc;\r
5639   COLORREF newcolor;\r
5640   int i;\r
5641   ColorClass ccl;\r
5642 \r
5643   if (firstTime) {\r
5644     /* Make initial colors in use available as custom colors */\r
5645     /* Should we put the compiled-in defaults here instead? */\r
5646     i = 0;\r
5647     customColors[i++] = lightSquareColor & 0xffffff;\r
5648     customColors[i++] = darkSquareColor & 0xffffff;\r
5649     customColors[i++] = whitePieceColor & 0xffffff;\r
5650     customColors[i++] = blackPieceColor & 0xffffff;\r
5651     customColors[i++] = highlightSquareColor & 0xffffff;\r
5652     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5653 \r
5654     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5655       customColors[i++] = textAttribs[ccl].color;\r
5656     }\r
5657     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5658     firstTime = FALSE;\r
5659   }\r
5660 \r
5661   cc.lStructSize = sizeof(cc);\r
5662   cc.hwndOwner = hwnd;\r
5663   cc.hInstance = NULL;\r
5664   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5665   cc.lpCustColors = (LPDWORD) customColors;\r
5666   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5667 \r
5668   if (!ChooseColor(&cc)) return FALSE;\r
5669 \r
5670   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5671   if (newcolor == *which) return FALSE;\r
5672   *which = newcolor;\r
5673   return TRUE;\r
5674 \r
5675   /*\r
5676   InitDrawingColors();\r
5677   InvalidateRect(hwnd, &boardRect, FALSE);\r
5678   */\r
5679 }\r
5680 \r
5681 BOOLEAN\r
5682 MyLoadSound(MySound *ms)\r
5683 {\r
5684   BOOL ok = FALSE;\r
5685   struct stat st;\r
5686   FILE *f;\r
5687 \r
5688   if (ms->data && ms->flag) free(ms->data);\r
5689   ms->data = NULL;\r
5690 \r
5691   switch (ms->name[0]) {\r
5692   case NULLCHAR:\r
5693     /* Silence */\r
5694     ok = TRUE;\r
5695     break;\r
5696   case '$':\r
5697     /* System sound from Control Panel.  Don't preload here. */\r
5698     ok = TRUE;\r
5699     break;\r
5700   case '!':\r
5701     if (ms->name[1] == NULLCHAR) {\r
5702       /* "!" alone = silence */\r
5703       ok = TRUE;\r
5704     } else {\r
5705       /* Builtin wave resource.  Error if not found. */\r
5706       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5707       if (h == NULL) break;\r
5708       ms->data = (void *)LoadResource(hInst, h);\r
5709       ms->flag = 0; // not maloced, so cannot be freed!\r
5710       if (h == NULL) break;\r
5711       ok = TRUE;\r
5712     }\r
5713     break;\r
5714   default:\r
5715     /* .wav file.  Error if not found. */\r
5716     f = fopen(ms->name, "rb");\r
5717     if (f == NULL) break;\r
5718     if (fstat(fileno(f), &st) < 0) break;\r
5719     ms->data = malloc(st.st_size);\r
5720     ms->flag = 1;\r
5721     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5722     fclose(f);\r
5723     ok = TRUE;\r
5724     break;\r
5725   }\r
5726   if (!ok) {\r
5727     char buf[MSG_SIZ];\r
5728       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5729     DisplayError(buf, GetLastError());\r
5730   }\r
5731   return ok;\r
5732 }\r
5733 \r
5734 BOOLEAN\r
5735 MyPlaySound(MySound *ms)\r
5736 {\r
5737   BOOLEAN ok = FALSE;\r
5738 \r
5739   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5740   switch (ms->name[0]) {\r
5741   case NULLCHAR:\r
5742         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5743     /* Silence */\r
5744     ok = TRUE;\r
5745     break;\r
5746   case '$':\r
5747     /* System sound from Control Panel (deprecated feature).\r
5748        "$" alone or an unset sound name gets default beep (still in use). */\r
5749     if (ms->name[1]) {\r
5750       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5751     }\r
5752     if (!ok) ok = MessageBeep(MB_OK);\r
5753     break; \r
5754   case '!':\r
5755     /* Builtin wave resource, or "!" alone for silence */\r
5756     if (ms->name[1]) {\r
5757       if (ms->data == NULL) return FALSE;\r
5758       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5759     } else {\r
5760       ok = TRUE;\r
5761     }\r
5762     break;\r
5763   default:\r
5764     /* .wav file.  Error if not found. */\r
5765     if (ms->data == NULL) return FALSE;\r
5766     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5767     break;\r
5768   }\r
5769   /* Don't print an error: this can happen innocently if the sound driver\r
5770      is busy; for instance, if another instance of WinBoard is playing\r
5771      a sound at about the same time. */\r
5772   return ok;\r
5773 }\r
5774 \r
5775 \r
5776 LRESULT CALLBACK\r
5777 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5778 {\r
5779   BOOL ok;\r
5780   OPENFILENAME *ofn;\r
5781   static UINT *number; /* gross that this is static */\r
5782 \r
5783   switch (message) {\r
5784   case WM_INITDIALOG: /* message: initialize dialog box */\r
5785     /* Center the dialog over the application window */\r
5786     ofn = (OPENFILENAME *) lParam;\r
5787     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5788       number = (UINT *) ofn->lCustData;\r
5789       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5790     } else {\r
5791       number = NULL;\r
5792     }\r
5793     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5794     Translate(hDlg, 1536);\r
5795     return FALSE;  /* Allow for further processing */\r
5796 \r
5797   case WM_COMMAND:\r
5798     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5799       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5800     }\r
5801     return FALSE;  /* Allow for further processing */\r
5802   }\r
5803   return FALSE;\r
5804 }\r
5805 \r
5806 UINT APIENTRY\r
5807 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5808 {\r
5809   static UINT *number;\r
5810   OPENFILENAME *ofname;\r
5811   OFNOTIFY *ofnot;\r
5812   switch (uiMsg) {\r
5813   case WM_INITDIALOG:\r
5814     Translate(hdlg, DLG_IndexNumber);\r
5815     ofname = (OPENFILENAME *)lParam;\r
5816     number = (UINT *)(ofname->lCustData);\r
5817     break;\r
5818   case WM_NOTIFY:\r
5819     ofnot = (OFNOTIFY *)lParam;\r
5820     if (ofnot->hdr.code == CDN_FILEOK) {\r
5821       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5822     }\r
5823     break;\r
5824   }\r
5825   return 0;\r
5826 }\r
5827 \r
5828 \r
5829 FILE *\r
5830 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5831                char *nameFilt, char *dlgTitle, UINT *number,\r
5832                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5833 {\r
5834   OPENFILENAME openFileName;\r
5835   char buf1[MSG_SIZ];\r
5836   FILE *f;\r
5837 \r
5838   if (fileName == NULL) fileName = buf1;\r
5839   if (defName == NULL) {\r
5840     safeStrCpy(fileName, "*.", 3 );\r
5841     strcat(fileName, defExt);\r
5842   } else {\r
5843     safeStrCpy(fileName, defName, MSG_SIZ );\r
5844   }\r
5845     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5846   if (number) *number = 0;\r
5847 \r
5848   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5849   openFileName.hwndOwner         = hwnd;\r
5850   openFileName.hInstance         = (HANDLE) hInst;\r
5851   openFileName.lpstrFilter       = nameFilt;\r
5852   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5853   openFileName.nMaxCustFilter    = 0L;\r
5854   openFileName.nFilterIndex      = 1L;\r
5855   openFileName.lpstrFile         = fileName;\r
5856   openFileName.nMaxFile          = MSG_SIZ;\r
5857   openFileName.lpstrFileTitle    = fileTitle;\r
5858   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5859   openFileName.lpstrInitialDir   = NULL;\r
5860   openFileName.lpstrTitle        = dlgTitle;\r
5861   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5862     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5863     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5864     | (oldDialog ? 0 : OFN_EXPLORER);\r
5865   openFileName.nFileOffset       = 0;\r
5866   openFileName.nFileExtension    = 0;\r
5867   openFileName.lpstrDefExt       = defExt;\r
5868   openFileName.lCustData         = (LONG) number;\r
5869   openFileName.lpfnHook          = oldDialog ?\r
5870     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5871   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5872 \r
5873   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5874                         GetOpenFileName(&openFileName)) {\r
5875     /* open the file */\r
5876     f = fopen(openFileName.lpstrFile, write);\r
5877     if (f == NULL) {\r
5878       MessageBox(hwnd, _("File open failed"), NULL,\r
5879                  MB_OK|MB_ICONEXCLAMATION);\r
5880       return NULL;\r
5881     }\r
5882   } else {\r
5883     int err = CommDlgExtendedError();\r
5884     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5885     return FALSE;\r
5886   }\r
5887   return f;\r
5888 }\r
5889 \r
5890 \r
5891 \r
5892 VOID APIENTRY\r
5893 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5894 {\r
5895   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5896 \r
5897   /*\r
5898    * Get the first pop-up menu in the menu template. This is the\r
5899    * menu that TrackPopupMenu displays.\r
5900    */\r
5901   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5902   TranslateOneMenu(10, hmenuTrackPopup);\r
5903 \r
5904   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5905 \r
5906   /*\r
5907    * TrackPopup uses screen coordinates, so convert the\r
5908    * coordinates of the mouse click to screen coordinates.\r
5909    */\r
5910   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5911 \r
5912   /* Draw and track the floating pop-up menu. */\r
5913   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5914                  pt.x, pt.y, 0, hwnd, NULL);\r
5915 \r
5916   /* Destroy the menu.*/\r
5917   DestroyMenu(hmenu);\r
5918 }\r
5919    \r
5920 typedef struct {\r
5921   HWND hDlg, hText;\r
5922   int sizeX, sizeY, newSizeX, newSizeY;\r
5923   HDWP hdwp;\r
5924 } ResizeEditPlusButtonsClosure;\r
5925 \r
5926 BOOL CALLBACK\r
5927 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5928 {\r
5929   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5930   RECT rect;\r
5931   POINT pt;\r
5932 \r
5933   if (hChild == cl->hText) return TRUE;\r
5934   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5935   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5936   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5937   ScreenToClient(cl->hDlg, &pt);\r
5938   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5939     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5940   return TRUE;\r
5941 }\r
5942 \r
5943 /* Resize a dialog that has a (rich) edit field filling most of\r
5944    the top, with a row of buttons below */\r
5945 VOID\r
5946 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5947 {\r
5948   RECT rectText;\r
5949   int newTextHeight, newTextWidth;\r
5950   ResizeEditPlusButtonsClosure cl;\r
5951   \r
5952   /*if (IsIconic(hDlg)) return;*/\r
5953   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5954   \r
5955   cl.hdwp = BeginDeferWindowPos(8);\r
5956 \r
5957   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5958   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5959   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5960   if (newTextHeight < 0) {\r
5961     newSizeY += -newTextHeight;\r
5962     newTextHeight = 0;\r
5963   }\r
5964   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5965     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5966 \r
5967   cl.hDlg = hDlg;\r
5968   cl.hText = hText;\r
5969   cl.sizeX = sizeX;\r
5970   cl.sizeY = sizeY;\r
5971   cl.newSizeX = newSizeX;\r
5972   cl.newSizeY = newSizeY;\r
5973   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5974 \r
5975   EndDeferWindowPos(cl.hdwp);\r
5976 }\r
5977 \r
5978 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5979 {\r
5980     RECT    rChild, rParent;\r
5981     int     wChild, hChild, wParent, hParent;\r
5982     int     wScreen, hScreen, xNew, yNew;\r
5983     HDC     hdc;\r
5984 \r
5985     /* Get the Height and Width of the child window */\r
5986     GetWindowRect (hwndChild, &rChild);\r
5987     wChild = rChild.right - rChild.left;\r
5988     hChild = rChild.bottom - rChild.top;\r
5989 \r
5990     /* Get the Height and Width of the parent window */\r
5991     GetWindowRect (hwndParent, &rParent);\r
5992     wParent = rParent.right - rParent.left;\r
5993     hParent = rParent.bottom - rParent.top;\r
5994 \r
5995     /* Get the display limits */\r
5996     hdc = GetDC (hwndChild);\r
5997     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5998     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5999     ReleaseDC(hwndChild, hdc);\r
6000 \r
6001     /* Calculate new X position, then adjust for screen */\r
6002     xNew = rParent.left + ((wParent - wChild) /2);\r
6003     if (xNew < 0) {\r
6004         xNew = 0;\r
6005     } else if ((xNew+wChild) > wScreen) {\r
6006         xNew = wScreen - wChild;\r
6007     }\r
6008 \r
6009     /* Calculate new Y position, then adjust for screen */\r
6010     if( mode == 0 ) {\r
6011         yNew = rParent.top  + ((hParent - hChild) /2);\r
6012     }\r
6013     else {\r
6014         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6015     }\r
6016 \r
6017     if (yNew < 0) {\r
6018         yNew = 0;\r
6019     } else if ((yNew+hChild) > hScreen) {\r
6020         yNew = hScreen - hChild;\r
6021     }\r
6022 \r
6023     /* Set it, and return */\r
6024     return SetWindowPos (hwndChild, NULL,\r
6025                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6026 }\r
6027 \r
6028 /* Center one window over another */\r
6029 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6030 {\r
6031     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6032 }\r
6033 \r
6034 /*---------------------------------------------------------------------------*\\r
6035  *\r
6036  * Startup Dialog functions\r
6037  *\r
6038 \*---------------------------------------------------------------------------*/\r
6039 void\r
6040 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6041 {\r
6042   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6043 \r
6044   while (*cd != NULL) {\r
6045     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6046     cd++;\r
6047   }\r
6048 }\r
6049 \r
6050 void\r
6051 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6052 {\r
6053   char buf1[MAX_ARG_LEN];\r
6054   int len;\r
6055 \r
6056   if (str[0] == '@') {\r
6057     FILE* f = fopen(str + 1, "r");\r
6058     if (f == NULL) {\r
6059       DisplayFatalError(str + 1, errno, 2);\r
6060       return;\r
6061     }\r
6062     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6063     fclose(f);\r
6064     buf1[len] = NULLCHAR;\r
6065     str = buf1;\r
6066   }\r
6067 \r
6068   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6069 \r
6070   for (;;) {\r
6071     char buf[MSG_SIZ];\r
6072     char *end = strchr(str, '\n');\r
6073     if (end == NULL) return;\r
6074     memcpy(buf, str, end - str);\r
6075     buf[end - str] = NULLCHAR;\r
6076     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6077     str = end + 1;\r
6078   }\r
6079 }\r
6080 \r
6081 void\r
6082 SetStartupDialogEnables(HWND hDlg)\r
6083 {\r
6084   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6085     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6086     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6087   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6088     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6089   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6090     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6091   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6092     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6093   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6094     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6095     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6096     IsDlgButtonChecked(hDlg, OPT_View));\r
6097 }\r
6098 \r
6099 char *\r
6100 QuoteForFilename(char *filename)\r
6101 {\r
6102   int dquote, space;\r
6103   dquote = strchr(filename, '"') != NULL;\r
6104   space = strchr(filename, ' ') != NULL;\r
6105   if (dquote || space) {\r
6106     if (dquote) {\r
6107       return "'";\r
6108     } else {\r
6109       return "\"";\r
6110     }\r
6111   } else {\r
6112     return "";\r
6113   }\r
6114 }\r
6115 \r
6116 VOID\r
6117 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6118 {\r
6119   char buf[MSG_SIZ];\r
6120   char *q;\r
6121 \r
6122   InitComboStringsFromOption(hwndCombo, nthnames);\r
6123   q = QuoteForFilename(nthcp);\r
6124     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6125   if (*nthdir != NULLCHAR) {\r
6126     q = QuoteForFilename(nthdir);\r
6127       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6128   }\r
6129   if (*nthcp == NULLCHAR) {\r
6130     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6131   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6132     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6133     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6134   }\r
6135 }\r
6136 \r
6137 LRESULT CALLBACK\r
6138 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6139 {\r
6140   char buf[MSG_SIZ];\r
6141   HANDLE hwndCombo;\r
6142   char *p;\r
6143 \r
6144   switch (message) {\r
6145   case WM_INITDIALOG:\r
6146     /* Center the dialog */\r
6147     CenterWindow (hDlg, GetDesktopWindow());\r
6148     Translate(hDlg, DLG_Startup);\r
6149     /* Initialize the dialog items */\r
6150     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6151                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6152                   firstChessProgramNames);\r
6153     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6154                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6155                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6156     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6157     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6158       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6159     if (*appData.icsHelper != NULLCHAR) {\r
6160       char *q = QuoteForFilename(appData.icsHelper);\r
6161       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6162     }\r
6163     if (*appData.icsHost == NULLCHAR) {\r
6164       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6165       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6166     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6167       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6168       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6169     }\r
6170 \r
6171     if (appData.icsActive) {\r
6172       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6173     }\r
6174     else if (appData.noChessProgram) {\r
6175       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6176     }\r
6177     else {\r
6178       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6179     }\r
6180 \r
6181     SetStartupDialogEnables(hDlg);\r
6182     return TRUE;\r
6183 \r
6184   case WM_COMMAND:\r
6185     switch (LOWORD(wParam)) {\r
6186     case IDOK:\r
6187       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6188         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6189         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6190         p = buf;\r
6191         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6192         ParseArgs(StringGet, &p);\r
6193         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6194         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6195         p = buf;\r
6196         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6197         ParseArgs(StringGet, &p);\r
6198         SwapEngines(singleList); // ... and then make it 'second'\r
6199         appData.noChessProgram = FALSE;\r
6200         appData.icsActive = FALSE;\r
6201       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6202         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6203         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6204         p = buf;\r
6205         ParseArgs(StringGet, &p);\r
6206         if (appData.zippyPlay) {\r
6207           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6208           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6209           p = buf;\r
6210           ParseArgs(StringGet, &p);\r
6211         }\r
6212       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6213         appData.noChessProgram = TRUE;\r
6214         appData.icsActive = FALSE;\r
6215       } else {\r
6216         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6217                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6218         return TRUE;\r
6219       }\r
6220       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6221         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6222         p = buf;\r
6223         ParseArgs(StringGet, &p);\r
6224       }\r
6225       EndDialog(hDlg, TRUE);\r
6226       return TRUE;\r
6227 \r
6228     case IDCANCEL:\r
6229       ExitEvent(0);\r
6230       return TRUE;\r
6231 \r
6232     case IDM_HELPCONTENTS:\r
6233       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6234         MessageBox (GetFocus(),\r
6235                     _("Unable to activate help"),\r
6236                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6237       }\r
6238       break;\r
6239 \r
6240     default:\r
6241       SetStartupDialogEnables(hDlg);\r
6242       break;\r
6243     }\r
6244     break;\r
6245   }\r
6246   return FALSE;\r
6247 }\r
6248 \r
6249 /*---------------------------------------------------------------------------*\\r
6250  *\r
6251  * About box dialog functions\r
6252  *\r
6253 \*---------------------------------------------------------------------------*/\r
6254 \r
6255 /* Process messages for "About" dialog box */\r
6256 LRESULT CALLBACK\r
6257 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6258 {\r
6259   switch (message) {\r
6260   case WM_INITDIALOG: /* message: initialize dialog box */\r
6261     /* Center the dialog over the application window */\r
6262     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6263     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6264     Translate(hDlg, ABOUTBOX);\r
6265     JAWS_COPYRIGHT\r
6266     return (TRUE);\r
6267 \r
6268   case WM_COMMAND: /* message: received a command */\r
6269     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6270         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6271       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6272       return (TRUE);\r
6273     }\r
6274     break;\r
6275   }\r
6276   return (FALSE);\r
6277 }\r
6278 \r
6279 /*---------------------------------------------------------------------------*\\r
6280  *\r
6281  * Comment Dialog functions\r
6282  *\r
6283 \*---------------------------------------------------------------------------*/\r
6284 \r
6285 LRESULT CALLBACK\r
6286 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6287 {\r
6288   static HANDLE hwndText = NULL;\r
6289   int len, newSizeX, newSizeY, flags;\r
6290   static int sizeX, sizeY;\r
6291   char *str;\r
6292   RECT rect;\r
6293   MINMAXINFO *mmi;\r
6294 \r
6295   switch (message) {\r
6296   case WM_INITDIALOG: /* message: initialize dialog box */\r
6297     /* Initialize the dialog items */\r
6298     Translate(hDlg, DLG_EditComment);\r
6299     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6300     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6301     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6302     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6303     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6304     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6305     SetWindowText(hDlg, commentTitle);\r
6306     if (editComment) {\r
6307       SetFocus(hwndText);\r
6308     } else {\r
6309       SetFocus(GetDlgItem(hDlg, IDOK));\r
6310     }\r
6311     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6312                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6313                 MAKELPARAM(FALSE, 0));\r
6314     /* Size and position the dialog */\r
6315     if (!commentDialog) {\r
6316       commentDialog = hDlg;\r
6317       flags = SWP_NOZORDER;\r
6318       GetClientRect(hDlg, &rect);\r
6319       sizeX = rect.right;\r
6320       sizeY = rect.bottom;\r
6321       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6322           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6323         WINDOWPLACEMENT wp;\r
6324         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6325         wp.length = sizeof(WINDOWPLACEMENT);\r
6326         wp.flags = 0;\r
6327         wp.showCmd = SW_SHOW;\r
6328         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6329         wp.rcNormalPosition.left = wpComment.x;\r
6330         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6331         wp.rcNormalPosition.top = wpComment.y;\r
6332         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6333         SetWindowPlacement(hDlg, &wp);\r
6334 \r
6335         GetClientRect(hDlg, &rect);\r
6336         newSizeX = rect.right;\r
6337         newSizeY = rect.bottom;\r
6338         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6339                               newSizeX, newSizeY);\r
6340         sizeX = newSizeX;\r
6341         sizeY = newSizeY;\r
6342       }\r
6343     }\r
6344     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6345     return FALSE;\r
6346 \r
6347   case WM_COMMAND: /* message: received a command */\r
6348     switch (LOWORD(wParam)) {\r
6349     case IDOK:\r
6350       if (editComment) {\r
6351         char *p, *q;\r
6352         /* Read changed options from the dialog box */\r
6353         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6354         len = GetWindowTextLength(hwndText);\r
6355         str = (char *) malloc(len + 1);\r
6356         GetWindowText(hwndText, str, len + 1);\r
6357         p = q = str;\r
6358         while (*q) {\r
6359           if (*q == '\r')\r
6360             q++;\r
6361           else\r
6362             *p++ = *q++;\r
6363         }\r
6364         *p = NULLCHAR;\r
6365         ReplaceComment(commentIndex, str);\r
6366         free(str);\r
6367       }\r
6368       CommentPopDown();\r
6369       return TRUE;\r
6370 \r
6371     case IDCANCEL:\r
6372     case OPT_CancelComment:\r
6373       CommentPopDown();\r
6374       return TRUE;\r
6375 \r
6376     case OPT_ClearComment:\r
6377       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6378       break;\r
6379 \r
6380     case OPT_EditComment:\r
6381       EditCommentEvent();\r
6382       return TRUE;\r
6383 \r
6384     default:\r
6385       break;\r
6386     }\r
6387     break;\r
6388 \r
6389   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6390         if( wParam == OPT_CommentText ) {\r
6391             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6392 \r
6393             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6394                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6395                 POINTL pt;\r
6396                 LRESULT index;\r
6397 \r
6398                 pt.x = LOWORD( lpMF->lParam );\r
6399                 pt.y = HIWORD( lpMF->lParam );\r
6400 \r
6401                 if(lpMF->msg == WM_CHAR) {\r
6402                         CHARRANGE sel;\r
6403                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6404                         index = sel.cpMin;\r
6405                 } else\r
6406                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6407 \r
6408                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6409                 len = GetWindowTextLength(hwndText);\r
6410                 str = (char *) malloc(len + 1);\r
6411                 GetWindowText(hwndText, str, len + 1);\r
6412                 ReplaceComment(commentIndex, str);\r
6413                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6414                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6415                 free(str);\r
6416 \r
6417                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6418                 lpMF->msg = WM_USER;\r
6419 \r
6420                 return TRUE;\r
6421             }\r
6422         }\r
6423         break;\r
6424 \r
6425   case WM_SIZE:\r
6426     newSizeX = LOWORD(lParam);\r
6427     newSizeY = HIWORD(lParam);\r
6428     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6429     sizeX = newSizeX;\r
6430     sizeY = newSizeY;\r
6431     break;\r
6432 \r
6433   case WM_GETMINMAXINFO:\r
6434     /* Prevent resizing window too small */\r
6435     mmi = (MINMAXINFO *) lParam;\r
6436     mmi->ptMinTrackSize.x = 100;\r
6437     mmi->ptMinTrackSize.y = 100;\r
6438     break;\r
6439   }\r
6440   return FALSE;\r
6441 }\r
6442 \r
6443 VOID\r
6444 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6445 {\r
6446   FARPROC lpProc;\r
6447   char *p, *q;\r
6448 \r
6449   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6450 \r
6451   if (str == NULL) str = "";\r
6452   p = (char *) malloc(2 * strlen(str) + 2);\r
6453   q = p;\r
6454   while (*str) {\r
6455     if (*str == '\n') *q++ = '\r';\r
6456     *q++ = *str++;\r
6457   }\r
6458   *q = NULLCHAR;\r
6459   if (commentText != NULL) free(commentText);\r
6460 \r
6461   commentIndex = index;\r
6462   commentTitle = title;\r
6463   commentText = p;\r
6464   editComment = edit;\r
6465 \r
6466   if (commentDialog) {\r
6467     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6468     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6469   } else {\r
6470     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6471     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6472                  hwndMain, (DLGPROC)lpProc);\r
6473     FreeProcInstance(lpProc);\r
6474   }\r
6475   commentUp = TRUE;\r
6476 }\r
6477 \r
6478 \r
6479 /*---------------------------------------------------------------------------*\\r
6480  *\r
6481  * Type-in move dialog functions\r
6482  * \r
6483 \*---------------------------------------------------------------------------*/\r
6484 \r
6485 LRESULT CALLBACK\r
6486 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6487 {\r
6488   char move[MSG_SIZ];\r
6489   HWND hInput;\r
6490 \r
6491   switch (message) {\r
6492   case WM_INITDIALOG:\r
6493     move[0] = (char) lParam;\r
6494     move[1] = NULLCHAR;\r
6495     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6496     Translate(hDlg, DLG_TypeInMove);\r
6497     hInput = GetDlgItem(hDlg, OPT_Move);\r
6498     SetWindowText(hInput, move);\r
6499     SetFocus(hInput);\r
6500     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6501     return FALSE;\r
6502 \r
6503   case WM_COMMAND:\r
6504     switch (LOWORD(wParam)) {\r
6505     case IDOK:\r
6506 \r
6507       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6508       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6509       TypeInDoneEvent(move);\r
6510       EndDialog(hDlg, TRUE);\r
6511       return TRUE;\r
6512     case IDCANCEL:\r
6513       EndDialog(hDlg, FALSE);\r
6514       return TRUE;\r
6515     default:\r
6516       break;\r
6517     }\r
6518     break;\r
6519   }\r
6520   return FALSE;\r
6521 }\r
6522 \r
6523 VOID\r
6524 PopUpMoveDialog(char firstchar)\r
6525 {\r
6526     FARPROC lpProc;\r
6527 \r
6528       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6529       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6530         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6531       FreeProcInstance(lpProc);\r
6532 }\r
6533 \r
6534 /*---------------------------------------------------------------------------*\\r
6535  *\r
6536  * Type-in name dialog functions\r
6537  * \r
6538 \*---------------------------------------------------------------------------*/\r
6539 \r
6540 LRESULT CALLBACK\r
6541 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6542 {\r
6543   char move[MSG_SIZ];\r
6544   HWND hInput;\r
6545 \r
6546   switch (message) {\r
6547   case WM_INITDIALOG:\r
6548     move[0] = (char) lParam;\r
6549     move[1] = NULLCHAR;\r
6550     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6551     Translate(hDlg, DLG_TypeInName);\r
6552     hInput = GetDlgItem(hDlg, OPT_Name);\r
6553     SetWindowText(hInput, move);\r
6554     SetFocus(hInput);\r
6555     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6556     return FALSE;\r
6557 \r
6558   case WM_COMMAND:\r
6559     switch (LOWORD(wParam)) {\r
6560     case IDOK:\r
6561       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6562       appData.userName = strdup(move);\r
6563       SetUserLogo();\r
6564       SetGameInfo();\r
6565       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6566         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6567         DisplayTitle(move);\r
6568       }\r
6569 \r
6570 \r
6571       EndDialog(hDlg, TRUE);\r
6572       return TRUE;\r
6573     case IDCANCEL:\r
6574       EndDialog(hDlg, FALSE);\r
6575       return TRUE;\r
6576     default:\r
6577       break;\r
6578     }\r
6579     break;\r
6580   }\r
6581   return FALSE;\r
6582 }\r
6583 \r
6584 VOID\r
6585 PopUpNameDialog(char firstchar)\r
6586 {\r
6587     FARPROC lpProc;\r
6588     \r
6589       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6590       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6591         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6592       FreeProcInstance(lpProc);\r
6593 }\r
6594 \r
6595 /*---------------------------------------------------------------------------*\\r
6596  *\r
6597  *  Error dialogs\r
6598  * \r
6599 \*---------------------------------------------------------------------------*/\r
6600 \r
6601 /* Nonmodal error box */\r
6602 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6603                              WPARAM wParam, LPARAM lParam);\r
6604 \r
6605 VOID\r
6606 ErrorPopUp(char *title, char *content)\r
6607 {\r
6608   FARPROC lpProc;\r
6609   char *p, *q;\r
6610   BOOLEAN modal = hwndMain == NULL;\r
6611 \r
6612   p = content;\r
6613   q = errorMessage;\r
6614   while (*p) {\r
6615     if (*p == '\n') {\r
6616       if (modal) {\r
6617         *q++ = ' ';\r
6618         p++;\r
6619       } else {\r
6620         *q++ = '\r';\r
6621         *q++ = *p++;\r
6622       }\r
6623     } else {\r
6624       *q++ = *p++;\r
6625     }\r
6626   }\r
6627   *q = NULLCHAR;\r
6628   strncpy(errorTitle, title, sizeof(errorTitle));\r
6629   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6630   \r
6631   if (modal) {\r
6632     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6633   } else {\r
6634     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6635     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6636                  hwndMain, (DLGPROC)lpProc);\r
6637     FreeProcInstance(lpProc);\r
6638   }\r
6639 }\r
6640 \r
6641 VOID\r
6642 ErrorPopDown()\r
6643 {\r
6644   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6645   if (errorDialog == NULL) return;\r
6646   DestroyWindow(errorDialog);\r
6647   errorDialog = NULL;\r
6648   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6649 }\r
6650 \r
6651 LRESULT CALLBACK\r
6652 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6653 {\r
6654   HANDLE hwndText;\r
6655   RECT rChild;\r
6656 \r
6657   switch (message) {\r
6658   case WM_INITDIALOG:\r
6659     GetWindowRect(hDlg, &rChild);\r
6660 \r
6661     /*\r
6662     SetWindowPos(hDlg, NULL, rChild.left,\r
6663       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6664       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6665     */\r
6666 \r
6667     /* \r
6668         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6669         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6670         and it doesn't work when you resize the dialog.\r
6671         For now, just give it a default position.\r
6672     */\r
6673     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6674     Translate(hDlg, DLG_Error);\r
6675 \r
6676     errorDialog = hDlg;\r
6677     SetWindowText(hDlg, errorTitle);\r
6678     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6679     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6680     return FALSE;\r
6681 \r
6682   case WM_COMMAND:\r
6683     switch (LOWORD(wParam)) {\r
6684     case IDOK:\r
6685     case IDCANCEL:\r
6686       if (errorDialog == hDlg) errorDialog = NULL;\r
6687       DestroyWindow(hDlg);\r
6688       return TRUE;\r
6689 \r
6690     default:\r
6691       break;\r
6692     }\r
6693     break;\r
6694   }\r
6695   return FALSE;\r
6696 }\r
6697 \r
6698 #ifdef GOTHIC\r
6699 HWND gothicDialog = NULL;\r
6700 \r
6701 LRESULT CALLBACK\r
6702 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6703 {\r
6704   HANDLE hwndText;\r
6705   RECT rChild;\r
6706   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6707 \r
6708   switch (message) {\r
6709   case WM_INITDIALOG:\r
6710     GetWindowRect(hDlg, &rChild);\r
6711 \r
6712     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6713                                                              SWP_NOZORDER);\r
6714 \r
6715     /* \r
6716         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6717         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6718         and it doesn't work when you resize the dialog.\r
6719         For now, just give it a default position.\r
6720     */\r
6721     gothicDialog = hDlg;\r
6722     SetWindowText(hDlg, errorTitle);\r
6723     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6724     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6725     return FALSE;\r
6726 \r
6727   case WM_COMMAND:\r
6728     switch (LOWORD(wParam)) {\r
6729     case IDOK:\r
6730     case IDCANCEL:\r
6731       if (errorDialog == hDlg) errorDialog = NULL;\r
6732       DestroyWindow(hDlg);\r
6733       return TRUE;\r
6734 \r
6735     default:\r
6736       break;\r
6737     }\r
6738     break;\r
6739   }\r
6740   return FALSE;\r
6741 }\r
6742 \r
6743 VOID\r
6744 GothicPopUp(char *title, VariantClass variant)\r
6745 {\r
6746   FARPROC lpProc;\r
6747   static char *lastTitle;\r
6748 \r
6749   strncpy(errorTitle, title, sizeof(errorTitle));\r
6750   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6751 \r
6752   if(lastTitle != title && gothicDialog != NULL) {\r
6753     DestroyWindow(gothicDialog);\r
6754     gothicDialog = NULL;\r
6755   }\r
6756   if(variant != VariantNormal && gothicDialog == NULL) {\r
6757     title = lastTitle;\r
6758     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6759     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6760                  hwndMain, (DLGPROC)lpProc);\r
6761     FreeProcInstance(lpProc);\r
6762   }\r
6763 }\r
6764 #endif\r
6765 \r
6766 /*---------------------------------------------------------------------------*\\r
6767  *\r
6768  *  Ics Interaction console functions\r
6769  *\r
6770 \*---------------------------------------------------------------------------*/\r
6771 \r
6772 #define HISTORY_SIZE 64\r
6773 static char *history[HISTORY_SIZE];\r
6774 int histIn = 0, histP = 0;\r
6775 \r
6776 VOID\r
6777 SaveInHistory(char *cmd)\r
6778 {\r
6779   if (history[histIn] != NULL) {\r
6780     free(history[histIn]);\r
6781     history[histIn] = NULL;\r
6782   }\r
6783   if (*cmd == NULLCHAR) return;\r
6784   history[histIn] = StrSave(cmd);\r
6785   histIn = (histIn + 1) % HISTORY_SIZE;\r
6786   if (history[histIn] != NULL) {\r
6787     free(history[histIn]);\r
6788     history[histIn] = NULL;\r
6789   }\r
6790   histP = histIn;\r
6791 }\r
6792 \r
6793 char *\r
6794 PrevInHistory(char *cmd)\r
6795 {\r
6796   int newhp;\r
6797   if (histP == histIn) {\r
6798     if (history[histIn] != NULL) free(history[histIn]);\r
6799     history[histIn] = StrSave(cmd);\r
6800   }\r
6801   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6802   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6803   histP = newhp;\r
6804   return history[histP];\r
6805 }\r
6806 \r
6807 char *\r
6808 NextInHistory()\r
6809 {\r
6810   if (histP == histIn) return NULL;\r
6811   histP = (histP + 1) % HISTORY_SIZE;\r
6812   return history[histP];   \r
6813 }\r
6814 \r
6815 HMENU\r
6816 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6817 {\r
6818   HMENU hmenu, h;\r
6819   int i = 0;\r
6820   hmenu = LoadMenu(hInst, "TextMenu");\r
6821   h = GetSubMenu(hmenu, 0);\r
6822   while (e->item) {\r
6823     if (strcmp(e->item, "-") == 0) {\r
6824       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6825     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6826       int flags = MF_STRING, j = 0;\r
6827       if (e->item[0] == '|') {\r
6828         flags |= MF_MENUBARBREAK;\r
6829         j++;\r
6830       }\r
6831       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6832       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6833     }\r
6834     e++;\r
6835     i++;\r
6836   } \r
6837   return hmenu;\r
6838 }\r
6839 \r
6840 WNDPROC consoleTextWindowProc;\r
6841 \r
6842 void\r
6843 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6844 {\r
6845   char buf[MSG_SIZ], name[MSG_SIZ];\r
6846   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6847   CHARRANGE sel;\r
6848 \r
6849   if (!getname) {\r
6850     SetWindowText(hInput, command);\r
6851     if (immediate) {\r
6852       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6853     } else {\r
6854       sel.cpMin = 999999;\r
6855       sel.cpMax = 999999;\r
6856       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6857       SetFocus(hInput);\r
6858     }\r
6859     return;\r
6860   }    \r
6861   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6862   if (sel.cpMin == sel.cpMax) {\r
6863     /* Expand to surrounding word */\r
6864     TEXTRANGE tr;\r
6865     do {\r
6866       tr.chrg.cpMax = sel.cpMin;\r
6867       tr.chrg.cpMin = --sel.cpMin;\r
6868       if (sel.cpMin < 0) break;\r
6869       tr.lpstrText = name;\r
6870       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6871     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6872     sel.cpMin++;\r
6873 \r
6874     do {\r
6875       tr.chrg.cpMin = sel.cpMax;\r
6876       tr.chrg.cpMax = ++sel.cpMax;\r
6877       tr.lpstrText = name;\r
6878       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6879     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6880     sel.cpMax--;\r
6881 \r
6882     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6883       MessageBeep(MB_ICONEXCLAMATION);\r
6884       return;\r
6885     }\r
6886     tr.chrg = sel;\r
6887     tr.lpstrText = name;\r
6888     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6889   } else {\r
6890     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6891       MessageBeep(MB_ICONEXCLAMATION);\r
6892       return;\r
6893     }\r
6894     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6895   }\r
6896   if (immediate) {\r
6897     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6898     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6899     SetWindowText(hInput, buf);\r
6900     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6901   } else {\r
6902     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6903       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6904     SetWindowText(hInput, buf);\r
6905     sel.cpMin = 999999;\r
6906     sel.cpMax = 999999;\r
6907     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6908     SetFocus(hInput);\r
6909   }\r
6910 }\r
6911 \r
6912 LRESULT CALLBACK \r
6913 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6914 {\r
6915   HWND hInput;\r
6916   CHARRANGE sel;\r
6917 \r
6918   switch (message) {\r
6919   case WM_KEYDOWN:\r
6920     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6921     if(wParam=='R') return 0;\r
6922     switch (wParam) {\r
6923     case VK_PRIOR:\r
6924       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6925       return 0;\r
6926     case VK_NEXT:\r
6927       sel.cpMin = 999999;\r
6928       sel.cpMax = 999999;\r
6929       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6930       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6931       return 0;\r
6932     }\r
6933     break;\r
6934   case WM_CHAR:\r
6935    if(wParam != '\022') {\r
6936     if (wParam == '\t') {\r
6937       if (GetKeyState(VK_SHIFT) < 0) {\r
6938         /* shifted */\r
6939         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6940         if (buttonDesc[0].hwnd) {\r
6941           SetFocus(buttonDesc[0].hwnd);\r
6942         } else {\r
6943           SetFocus(hwndMain);\r
6944         }\r
6945       } else {\r
6946         /* unshifted */\r
6947         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6948       }\r
6949     } else {\r
6950       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6951       JAWS_DELETE( SetFocus(hInput); )\r
6952       SendMessage(hInput, message, wParam, lParam);\r
6953     }\r
6954     return 0;\r
6955    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6956    lParam = -1;\r
6957   case WM_RBUTTONDOWN:\r
6958     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6959       /* Move selection here if it was empty */\r
6960       POINT pt;\r
6961       pt.x = LOWORD(lParam);\r
6962       pt.y = HIWORD(lParam);\r
6963       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6964       if (sel.cpMin == sel.cpMax) {\r
6965         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6966         sel.cpMax = sel.cpMin;\r
6967         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6968       }\r
6969       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6970 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6971       POINT pt;\r
6972       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6973       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6974       if (sel.cpMin == sel.cpMax) {\r
6975         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6976         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6977       }\r
6978       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6979         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6980       }\r
6981       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6982       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6983       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6984       MenuPopup(hwnd, pt, hmenu, -1);\r
6985 }\r
6986     }\r
6987     return 0;\r
6988   case WM_RBUTTONUP:\r
6989     if (GetKeyState(VK_SHIFT) & ~1) {\r
6990       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6991         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6992     }\r
6993     return 0;\r
6994   case WM_PASTE:\r
6995     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6996     SetFocus(hInput);\r
6997     return SendMessage(hInput, message, wParam, lParam);\r
6998   case WM_MBUTTONDOWN:\r
6999     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7000   case WM_COMMAND:\r
7001     switch (LOWORD(wParam)) {\r
7002     case IDM_QuickPaste:\r
7003       {\r
7004         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7005         if (sel.cpMin == sel.cpMax) {\r
7006           MessageBeep(MB_ICONEXCLAMATION);\r
7007           return 0;\r
7008         }\r
7009         SendMessage(hwnd, WM_COPY, 0, 0);\r
7010         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7011         SendMessage(hInput, WM_PASTE, 0, 0);\r
7012         SetFocus(hInput);\r
7013         return 0;\r
7014       }\r
7015     case IDM_Cut:\r
7016       SendMessage(hwnd, WM_CUT, 0, 0);\r
7017       return 0;\r
7018     case IDM_Paste:\r
7019       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7020       return 0;\r
7021     case IDM_Copy:\r
7022       SendMessage(hwnd, WM_COPY, 0, 0);\r
7023       return 0;\r
7024     default:\r
7025       {\r
7026         int i = LOWORD(wParam) - IDM_CommandX;\r
7027         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7028             icsTextMenuEntry[i].command != NULL) {\r
7029           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7030                    icsTextMenuEntry[i].getname,\r
7031                    icsTextMenuEntry[i].immediate);\r
7032           return 0;\r
7033         }\r
7034       }\r
7035       break;\r
7036     }\r
7037     break;\r
7038   }\r
7039   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7040 }\r
7041 \r
7042 WNDPROC consoleInputWindowProc;\r
7043 \r
7044 LRESULT CALLBACK\r
7045 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7046 {\r
7047   char buf[MSG_SIZ];\r
7048   char *p;\r
7049   static BOOL sendNextChar = FALSE;\r
7050   static BOOL quoteNextChar = FALSE;\r
7051   InputSource *is = consoleInputSource;\r
7052   CHARFORMAT cf;\r
7053   CHARRANGE sel;\r
7054 \r
7055   switch (message) {\r
7056   case WM_CHAR:\r
7057     if (!appData.localLineEditing || sendNextChar) {\r
7058       is->buf[0] = (CHAR) wParam;\r
7059       is->count = 1;\r
7060       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7061       sendNextChar = FALSE;\r
7062       return 0;\r
7063     }\r
7064     if (quoteNextChar) {\r
7065       buf[0] = (char) wParam;\r
7066       buf[1] = NULLCHAR;\r
7067       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7068       quoteNextChar = FALSE;\r
7069       return 0;\r
7070     }\r
7071     switch (wParam) {\r
7072     case '\r':   /* Enter key */\r
7073       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7074       if (consoleEcho) SaveInHistory(is->buf);\r
7075       is->buf[is->count++] = '\n';\r
7076       is->buf[is->count] = NULLCHAR;\r
7077       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7078       if (consoleEcho) {\r
7079         ConsoleOutput(is->buf, is->count, TRUE);\r
7080       } else if (appData.localLineEditing) {\r
7081         ConsoleOutput("\n", 1, TRUE);\r
7082       }\r
7083       /* fall thru */\r
7084     case '\033': /* Escape key */\r
7085       SetWindowText(hwnd, "");\r
7086       cf.cbSize = sizeof(CHARFORMAT);\r
7087       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7088       if (consoleEcho) {\r
7089         cf.crTextColor = textAttribs[ColorNormal].color;\r
7090       } else {\r
7091         cf.crTextColor = COLOR_ECHOOFF;\r
7092       }\r
7093       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7094       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7095       return 0;\r
7096     case '\t':   /* Tab key */\r
7097       if (GetKeyState(VK_SHIFT) < 0) {\r
7098         /* shifted */\r
7099         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7100       } else {\r
7101         /* unshifted */\r
7102         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7103         if (buttonDesc[0].hwnd) {\r
7104           SetFocus(buttonDesc[0].hwnd);\r
7105         } else {\r
7106           SetFocus(hwndMain);\r
7107         }\r
7108       }\r
7109       return 0;\r
7110     case '\023': /* Ctrl+S */\r
7111       sendNextChar = TRUE;\r
7112       return 0;\r
7113     case '\021': /* Ctrl+Q */\r
7114       quoteNextChar = TRUE;\r
7115       return 0;\r
7116     JAWS_REPLAY\r
7117     default:\r
7118       break;\r
7119     }\r
7120     break;\r
7121   case WM_KEYDOWN:\r
7122     switch (wParam) {\r
7123     case VK_UP:\r
7124       GetWindowText(hwnd, buf, MSG_SIZ);\r
7125       p = PrevInHistory(buf);\r
7126       if (p != NULL) {\r
7127         SetWindowText(hwnd, p);\r
7128         sel.cpMin = 999999;\r
7129         sel.cpMax = 999999;\r
7130         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7131         return 0;\r
7132       }\r
7133       break;\r
7134     case VK_DOWN:\r
7135       p = NextInHistory();\r
7136       if (p != NULL) {\r
7137         SetWindowText(hwnd, p);\r
7138         sel.cpMin = 999999;\r
7139         sel.cpMax = 999999;\r
7140         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7141         return 0;\r
7142       }\r
7143       break;\r
7144     case VK_HOME:\r
7145     case VK_END:\r
7146       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7147       /* fall thru */\r
7148     case VK_PRIOR:\r
7149     case VK_NEXT:\r
7150       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7151       return 0;\r
7152     }\r
7153     break;\r
7154   case WM_MBUTTONDOWN:\r
7155     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7156       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7157     break;\r
7158   case WM_RBUTTONUP:\r
7159     if (GetKeyState(VK_SHIFT) & ~1) {\r
7160       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7161         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7162     } else {\r
7163       POINT pt;\r
7164       HMENU hmenu;\r
7165       hmenu = LoadMenu(hInst, "InputMenu");\r
7166       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7167       if (sel.cpMin == sel.cpMax) {\r
7168         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7169         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7170       }\r
7171       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7172         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7173       }\r
7174       pt.x = LOWORD(lParam);\r
7175       pt.y = HIWORD(lParam);\r
7176       MenuPopup(hwnd, pt, hmenu, -1);\r
7177     }\r
7178     return 0;\r
7179   case WM_COMMAND:\r
7180     switch (LOWORD(wParam)) { \r
7181     case IDM_Undo:\r
7182       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7183       return 0;\r
7184     case IDM_SelectAll:\r
7185       sel.cpMin = 0;\r
7186       sel.cpMax = -1; /*999999?*/\r
7187       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7188       return 0;\r
7189     case IDM_Cut:\r
7190       SendMessage(hwnd, WM_CUT, 0, 0);\r
7191       return 0;\r
7192     case IDM_Paste:\r
7193       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7194       return 0;\r
7195     case IDM_Copy:\r
7196       SendMessage(hwnd, WM_COPY, 0, 0);\r
7197       return 0;\r
7198     }\r
7199     break;\r
7200   }\r
7201   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7202 }\r
7203 \r
7204 #define CO_MAX  100000\r
7205 #define CO_TRIM   1000\r
7206 \r
7207 LRESULT CALLBACK\r
7208 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7209 {\r
7210   static SnapData sd;\r
7211   HWND hText, hInput;\r
7212   RECT rect;\r
7213   static int sizeX, sizeY;\r
7214   int newSizeX, newSizeY;\r
7215   MINMAXINFO *mmi;\r
7216   WORD wMask;\r
7217 \r
7218   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7219   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7220 \r
7221   switch (message) {\r
7222   case WM_NOTIFY:\r
7223     if (((NMHDR*)lParam)->code == EN_LINK)\r
7224     {\r
7225       ENLINK *pLink = (ENLINK*)lParam;\r
7226       if (pLink->msg == WM_LBUTTONUP)\r
7227       {\r
7228         TEXTRANGE tr;\r
7229 \r
7230         tr.chrg = pLink->chrg;\r
7231         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7232         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7233         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7234         free(tr.lpstrText);\r
7235       }\r
7236     }\r
7237     break;\r
7238   case WM_INITDIALOG: /* message: initialize dialog box */\r
7239     hwndConsole = hDlg;\r
7240     SetFocus(hInput);\r
7241     consoleTextWindowProc = (WNDPROC)\r
7242       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7243     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7244     consoleInputWindowProc = (WNDPROC)\r
7245       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7246     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7247     Colorize(ColorNormal, TRUE);\r
7248     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7249     ChangedConsoleFont();\r
7250     GetClientRect(hDlg, &rect);\r
7251     sizeX = rect.right;\r
7252     sizeY = rect.bottom;\r
7253     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7254         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7255       WINDOWPLACEMENT wp;\r
7256       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7257       wp.length = sizeof(WINDOWPLACEMENT);\r
7258       wp.flags = 0;\r
7259       wp.showCmd = SW_SHOW;\r
7260       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7261       wp.rcNormalPosition.left = wpConsole.x;\r
7262       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7263       wp.rcNormalPosition.top = wpConsole.y;\r
7264       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7265       SetWindowPlacement(hDlg, &wp);\r
7266     }\r
7267 \r
7268    // [HGM] Chessknight's change 2004-07-13\r
7269    else { /* Determine Defaults */\r
7270        WINDOWPLACEMENT wp;\r
7271        wpConsole.x = wpMain.width + 1;\r
7272        wpConsole.y = wpMain.y;\r
7273        wpConsole.width = screenWidth -  wpMain.width;\r
7274        wpConsole.height = wpMain.height;\r
7275        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7276        wp.length = sizeof(WINDOWPLACEMENT);\r
7277        wp.flags = 0;\r
7278        wp.showCmd = SW_SHOW;\r
7279        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7280        wp.rcNormalPosition.left = wpConsole.x;\r
7281        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7282        wp.rcNormalPosition.top = wpConsole.y;\r
7283        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7284        SetWindowPlacement(hDlg, &wp);\r
7285     }\r
7286 \r
7287    // Allow hText to highlight URLs and send notifications on them\r
7288    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7289    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7290    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7291    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7292 \r
7293     return FALSE;\r
7294 \r
7295   case WM_SETFOCUS:\r
7296     SetFocus(hInput);\r
7297     return 0;\r
7298 \r
7299   case WM_CLOSE:\r
7300     ExitEvent(0);\r
7301     /* not reached */\r
7302     break;\r
7303 \r
7304   case WM_SIZE:\r
7305     if (IsIconic(hDlg)) break;\r
7306     newSizeX = LOWORD(lParam);\r
7307     newSizeY = HIWORD(lParam);\r
7308     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7309       RECT rectText, rectInput;\r
7310       POINT pt;\r
7311       int newTextHeight, newTextWidth;\r
7312       GetWindowRect(hText, &rectText);\r
7313       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7314       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7315       if (newTextHeight < 0) {\r
7316         newSizeY += -newTextHeight;\r
7317         newTextHeight = 0;\r
7318       }\r
7319       SetWindowPos(hText, NULL, 0, 0,\r
7320         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7321       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7322       pt.x = rectInput.left;\r
7323       pt.y = rectInput.top + newSizeY - sizeY;\r
7324       ScreenToClient(hDlg, &pt);\r
7325       SetWindowPos(hInput, NULL, \r
7326         pt.x, pt.y, /* needs client coords */   \r
7327         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7328         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7329     }\r
7330     sizeX = newSizeX;\r
7331     sizeY = newSizeY;\r
7332     break;\r
7333 \r
7334   case WM_GETMINMAXINFO:\r
7335     /* Prevent resizing window too small */\r
7336     mmi = (MINMAXINFO *) lParam;\r
7337     mmi->ptMinTrackSize.x = 100;\r
7338     mmi->ptMinTrackSize.y = 100;\r
7339     break;\r
7340 \r
7341   /* [AS] Snapping */\r
7342   case WM_ENTERSIZEMOVE:\r
7343     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7344 \r
7345   case WM_SIZING:\r
7346     return OnSizing( &sd, hDlg, wParam, lParam );\r
7347 \r
7348   case WM_MOVING:\r
7349     return OnMoving( &sd, hDlg, wParam, lParam );\r
7350 \r
7351   case WM_EXITSIZEMOVE:\r
7352         UpdateICSWidth(hText);\r
7353     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7354   }\r
7355 \r
7356   return DefWindowProc(hDlg, message, wParam, lParam);\r
7357 }\r
7358 \r
7359 \r
7360 VOID\r
7361 ConsoleCreate()\r
7362 {\r
7363   HWND hCons;\r
7364   if (hwndConsole) return;\r
7365   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7366   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7367 }\r
7368 \r
7369 \r
7370 VOID\r
7371 ConsoleOutput(char* data, int length, int forceVisible)\r
7372 {\r
7373   HWND hText;\r
7374   int trim, exlen;\r
7375   char *p, *q;\r
7376   char buf[CO_MAX+1];\r
7377   POINT pEnd;\r
7378   RECT rect;\r
7379   static int delayLF = 0;\r
7380   CHARRANGE savesel, sel;\r
7381 \r
7382   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7383   p = data;\r
7384   q = buf;\r
7385   if (delayLF) {\r
7386     *q++ = '\r';\r
7387     *q++ = '\n';\r
7388     delayLF = 0;\r
7389   }\r
7390   while (length--) {\r
7391     if (*p == '\n') {\r
7392       if (*++p) {\r
7393         *q++ = '\r';\r
7394         *q++ = '\n';\r
7395       } else {\r
7396         delayLF = 1;\r
7397       }\r
7398     } else if (*p == '\007') {\r
7399        MyPlaySound(&sounds[(int)SoundBell]);\r
7400        p++;\r
7401     } else {\r
7402       *q++ = *p++;\r
7403     }\r
7404   }\r
7405   *q = NULLCHAR;\r
7406   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7407   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7408   /* Save current selection */\r
7409   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7410   exlen = GetWindowTextLength(hText);\r
7411   /* Find out whether current end of text is visible */\r
7412   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7413   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7414   /* Trim existing text if it's too long */\r
7415   if (exlen + (q - buf) > CO_MAX) {\r
7416     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7417     sel.cpMin = 0;\r
7418     sel.cpMax = trim;\r
7419     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7420     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7421     exlen -= trim;\r
7422     savesel.cpMin -= trim;\r
7423     savesel.cpMax -= trim;\r
7424     if (exlen < 0) exlen = 0;\r
7425     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7426     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7427   }\r
7428   /* Append the new text */\r
7429   sel.cpMin = exlen;\r
7430   sel.cpMax = exlen;\r
7431   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7432   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7433   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7434   if (forceVisible || exlen == 0 ||\r
7435       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7436        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7437     /* Scroll to make new end of text visible if old end of text\r
7438        was visible or new text is an echo of user typein */\r
7439     sel.cpMin = 9999999;\r
7440     sel.cpMax = 9999999;\r
7441     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7442     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7443     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7444     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7445   }\r
7446   if (savesel.cpMax == exlen || forceVisible) {\r
7447     /* Move insert point to new end of text if it was at the old\r
7448        end of text or if the new text is an echo of user typein */\r
7449     sel.cpMin = 9999999;\r
7450     sel.cpMax = 9999999;\r
7451     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7452   } else {\r
7453     /* Restore previous selection */\r
7454     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7455   }\r
7456   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7457 }\r
7458 \r
7459 /*---------*/\r
7460 \r
7461 \r
7462 void\r
7463 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7464 {\r
7465   char buf[100];\r
7466   char *str;\r
7467   COLORREF oldFg, oldBg;\r
7468   HFONT oldFont;\r
7469   RECT rect;\r
7470 \r
7471   if(copyNumber > 1)\r
7472     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7473 \r
7474   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7475   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7476   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7477 \r
7478   rect.left = x;\r
7479   rect.right = x + squareSize;\r
7480   rect.top  = y;\r
7481   rect.bottom = y + squareSize;\r
7482   str = buf;\r
7483 \r
7484   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7485                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7486              y, ETO_CLIPPED|ETO_OPAQUE,\r
7487              &rect, str, strlen(str), NULL);\r
7488 \r
7489   (void) SetTextColor(hdc, oldFg);\r
7490   (void) SetBkColor(hdc, oldBg);\r
7491   (void) SelectObject(hdc, oldFont);\r
7492 }\r
7493 \r
7494 void\r
7495 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7496               RECT *rect, char *color, char *flagFell)\r
7497 {\r
7498   char buf[100];\r
7499   char *str;\r
7500   COLORREF oldFg, oldBg;\r
7501   HFONT oldFont;\r
7502 \r
7503   if (twoBoards && partnerUp) return;\r
7504   if (appData.clockMode) {\r
7505     if (tinyLayout)\r
7506       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7507     else\r
7508       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7509     str = buf;\r
7510   } else {\r
7511     str = color;\r
7512   }\r
7513 \r
7514   if (highlight) {\r
7515     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7516     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7517   } else {\r
7518     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7519     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7520   }\r
7521   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7522 \r
7523   JAWS_SILENCE\r
7524 \r
7525   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7526              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7527              rect, str, strlen(str), NULL);\r
7528   if(logoHeight > 0 && appData.clockMode) {\r
7529       RECT r;\r
7530       str += strlen(color)+2;\r
7531       r.top = rect->top + logoHeight/2;\r
7532       r.left = rect->left;\r
7533       r.right = rect->right;\r
7534       r.bottom = rect->bottom;\r
7535       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7536                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7537                  &r, str, strlen(str), NULL);\r
7538   }\r
7539   (void) SetTextColor(hdc, oldFg);\r
7540   (void) SetBkColor(hdc, oldBg);\r
7541   (void) SelectObject(hdc, oldFont);\r
7542 }\r
7543 \r
7544 \r
7545 int\r
7546 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7547            OVERLAPPED *ovl)\r
7548 {\r
7549   int ok, err;\r
7550 \r
7551   /* [AS]  */\r
7552   if( count <= 0 ) {\r
7553     if (appData.debugMode) {\r
7554       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7555     }\r
7556 \r
7557     return ERROR_INVALID_USER_BUFFER;\r
7558   }\r
7559 \r
7560   ResetEvent(ovl->hEvent);\r
7561   ovl->Offset = ovl->OffsetHigh = 0;\r
7562   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7563   if (ok) {\r
7564     err = NO_ERROR;\r
7565   } else {\r
7566     err = GetLastError();\r
7567     if (err == ERROR_IO_PENDING) {\r
7568       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7569       if (ok)\r
7570         err = NO_ERROR;\r
7571       else\r
7572         err = GetLastError();\r
7573     }\r
7574   }\r
7575   return err;\r
7576 }\r
7577 \r
7578 int\r
7579 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7580             OVERLAPPED *ovl)\r
7581 {\r
7582   int ok, err;\r
7583 \r
7584   ResetEvent(ovl->hEvent);\r
7585   ovl->Offset = ovl->OffsetHigh = 0;\r
7586   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7587   if (ok) {\r
7588     err = NO_ERROR;\r
7589   } else {\r
7590     err = GetLastError();\r
7591     if (err == ERROR_IO_PENDING) {\r
7592       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7593       if (ok)\r
7594         err = NO_ERROR;\r
7595       else\r
7596         err = GetLastError();\r
7597     }\r
7598   }\r
7599   return err;\r
7600 }\r
7601 \r
7602 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7603 void CheckForInputBufferFull( InputSource * is )\r
7604 {\r
7605     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7606         /* Look for end of line */\r
7607         char * p = is->buf;\r
7608         \r
7609         while( p < is->next && *p != '\n' ) {\r
7610             p++;\r
7611         }\r
7612 \r
7613         if( p >= is->next ) {\r
7614             if (appData.debugMode) {\r
7615                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7616             }\r
7617 \r
7618             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7619             is->count = (DWORD) -1;\r
7620             is->next = is->buf;\r
7621         }\r
7622     }\r
7623 }\r
7624 \r
7625 DWORD\r
7626 InputThread(LPVOID arg)\r
7627 {\r
7628   InputSource *is;\r
7629   OVERLAPPED ovl;\r
7630 \r
7631   is = (InputSource *) arg;\r
7632   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7633   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7634   while (is->hThread != NULL) {\r
7635     is->error = DoReadFile(is->hFile, is->next,\r
7636                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7637                            &is->count, &ovl);\r
7638     if (is->error == NO_ERROR) {\r
7639       is->next += is->count;\r
7640     } else {\r
7641       if (is->error == ERROR_BROKEN_PIPE) {\r
7642         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7643         is->count = 0;\r
7644       } else {\r
7645         is->count = (DWORD) -1;\r
7646         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7647         break; \r
7648       }\r
7649     }\r
7650 \r
7651     CheckForInputBufferFull( is );\r
7652 \r
7653     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7654 \r
7655     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7656 \r
7657     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7658   }\r
7659 \r
7660   CloseHandle(ovl.hEvent);\r
7661   CloseHandle(is->hFile);\r
7662 \r
7663   if (appData.debugMode) {\r
7664     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7665   }\r
7666 \r
7667   return 0;\r
7668 }\r
7669 \r
7670 \r
7671 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7672 DWORD\r
7673 NonOvlInputThread(LPVOID arg)\r
7674 {\r
7675   InputSource *is;\r
7676   char *p, *q;\r
7677   int i;\r
7678   char prev;\r
7679 \r
7680   is = (InputSource *) arg;\r
7681   while (is->hThread != NULL) {\r
7682     is->error = ReadFile(is->hFile, is->next,\r
7683                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7684                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7685     if (is->error == NO_ERROR) {\r
7686       /* Change CRLF to LF */\r
7687       if (is->next > is->buf) {\r
7688         p = is->next - 1;\r
7689         i = is->count + 1;\r
7690       } else {\r
7691         p = is->next;\r
7692         i = is->count;\r
7693       }\r
7694       q = p;\r
7695       prev = NULLCHAR;\r
7696       while (i > 0) {\r
7697         if (prev == '\r' && *p == '\n') {\r
7698           *(q-1) = '\n';\r
7699           is->count--;\r
7700         } else { \r
7701           *q++ = *p;\r
7702         }\r
7703         prev = *p++;\r
7704         i--;\r
7705       }\r
7706       *q = NULLCHAR;\r
7707       is->next = q;\r
7708     } else {\r
7709       if (is->error == ERROR_BROKEN_PIPE) {\r
7710         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7711         is->count = 0; \r
7712       } else {\r
7713         is->count = (DWORD) -1;\r
7714       }\r
7715     }\r
7716 \r
7717     CheckForInputBufferFull( is );\r
7718 \r
7719     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7720 \r
7721     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7722 \r
7723     if (is->count < 0) break;  /* Quit on error */\r
7724   }\r
7725   CloseHandle(is->hFile);\r
7726   return 0;\r
7727 }\r
7728 \r
7729 DWORD\r
7730 SocketInputThread(LPVOID arg)\r
7731 {\r
7732   InputSource *is;\r
7733 \r
7734   is = (InputSource *) arg;\r
7735   while (is->hThread != NULL) {\r
7736     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7737     if ((int)is->count == SOCKET_ERROR) {\r
7738       is->count = (DWORD) -1;\r
7739       is->error = WSAGetLastError();\r
7740     } else {\r
7741       is->error = NO_ERROR;\r
7742       is->next += is->count;\r
7743       if (is->count == 0 && is->second == is) {\r
7744         /* End of file on stderr; quit with no message */\r
7745         break;\r
7746       }\r
7747     }\r
7748     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7749 \r
7750     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7751 \r
7752     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7753   }\r
7754   return 0;\r
7755 }\r
7756 \r
7757 VOID\r
7758 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7759 {\r
7760   InputSource *is;\r
7761 \r
7762   is = (InputSource *) lParam;\r
7763   if (is->lineByLine) {\r
7764     /* Feed in lines one by one */\r
7765     char *p = is->buf;\r
7766     char *q = p;\r
7767     while (q < is->next) {\r
7768       if (*q++ == '\n') {\r
7769         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7770         p = q;\r
7771       }\r
7772     }\r
7773     \r
7774     /* Move any partial line to the start of the buffer */\r
7775     q = is->buf;\r
7776     while (p < is->next) {\r
7777       *q++ = *p++;\r
7778     }\r
7779     is->next = q;\r
7780 \r
7781     if (is->error != NO_ERROR || is->count == 0) {\r
7782       /* Notify backend of the error.  Note: If there was a partial\r
7783          line at the end, it is not flushed through. */\r
7784       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7785     }\r
7786   } else {\r
7787     /* Feed in the whole chunk of input at once */\r
7788     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7789     is->next = is->buf;\r
7790   }\r
7791 }\r
7792 \r
7793 /*---------------------------------------------------------------------------*\\r
7794  *\r
7795  *  Menu enables. Used when setting various modes.\r
7796  *\r
7797 \*---------------------------------------------------------------------------*/\r
7798 \r
7799 typedef struct {\r
7800   int item;\r
7801   int flags;\r
7802 } Enables;\r
7803 \r
7804 VOID\r
7805 GreyRevert(Boolean grey)\r
7806 { // [HGM] vari: for retracting variations in local mode\r
7807   HMENU hmenu = GetMenu(hwndMain);\r
7808   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7809   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7810 }\r
7811 \r
7812 VOID\r
7813 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7814 {\r
7815   while (enab->item > 0) {\r
7816     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7817     enab++;\r
7818   }\r
7819 }\r
7820 \r
7821 Enables gnuEnables[] = {\r
7822   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7835 \r
7836   // Needed to switch from ncp to GNU mode on Engine Load\r
7837   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7838   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7844   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7848   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7849   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7850   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7851   { -1, -1 }\r
7852 };\r
7853 \r
7854 Enables icsEnables[] = {\r
7855   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7875   { -1, -1 }\r
7876 };\r
7877 \r
7878 #if ZIPPY\r
7879 Enables zippyEnables[] = {\r
7880   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7883   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7884   { -1, -1 }\r
7885 };\r
7886 #endif\r
7887 \r
7888 Enables ncpEnables[] = {\r
7889   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7898   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7899   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7911   { -1, -1 }\r
7912 };\r
7913 \r
7914 Enables trainingOnEnables[] = {\r
7915   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7924   { -1, -1 }\r
7925 };\r
7926 \r
7927 Enables trainingOffEnables[] = {\r
7928   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7937   { -1, -1 }\r
7938 };\r
7939 \r
7940 /* These modify either ncpEnables or gnuEnables */\r
7941 Enables cmailEnables[] = {\r
7942   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7943   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7944   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7945   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7949   { -1, -1 }\r
7950 };\r
7951 \r
7952 Enables machineThinkingEnables[] = {\r
7953   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7966 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7969   { -1, -1 }\r
7970 };\r
7971 \r
7972 Enables userThinkingEnables[] = {\r
7973   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7986 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7989   { -1, -1 }\r
7990 };\r
7991 \r
7992 /*---------------------------------------------------------------------------*\\r
7993  *\r
7994  *  Front-end interface functions exported by XBoard.\r
7995  *  Functions appear in same order as prototypes in frontend.h.\r
7996  * \r
7997 \*---------------------------------------------------------------------------*/\r
7998 VOID\r
7999 CheckMark(UINT item, int state)\r
8000 {\r
8001     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8002 }\r
8003 \r
8004 VOID\r
8005 ModeHighlight()\r
8006 {\r
8007   static UINT prevChecked = 0;\r
8008   static int prevPausing = 0;\r
8009   UINT nowChecked;\r
8010 \r
8011   if (pausing != prevPausing) {\r
8012     prevPausing = pausing;\r
8013     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8014                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8015     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8016   }\r
8017 \r
8018   switch (gameMode) {\r
8019   case BeginningOfGame:\r
8020     if (appData.icsActive)\r
8021       nowChecked = IDM_IcsClient;\r
8022     else if (appData.noChessProgram)\r
8023       nowChecked = IDM_EditGame;\r
8024     else\r
8025       nowChecked = IDM_MachineBlack;\r
8026     break;\r
8027   case MachinePlaysBlack:\r
8028     nowChecked = IDM_MachineBlack;\r
8029     break;\r
8030   case MachinePlaysWhite:\r
8031     nowChecked = IDM_MachineWhite;\r
8032     break;\r
8033   case TwoMachinesPlay:\r
8034     nowChecked = IDM_TwoMachines;\r
8035     break;\r
8036   case AnalyzeMode:\r
8037     nowChecked = IDM_AnalysisMode;\r
8038     break;\r
8039   case AnalyzeFile:\r
8040     nowChecked = IDM_AnalyzeFile;\r
8041     break;\r
8042   case EditGame:\r
8043     nowChecked = IDM_EditGame;\r
8044     break;\r
8045   case PlayFromGameFile:\r
8046     nowChecked = IDM_LoadGame;\r
8047     break;\r
8048   case EditPosition:\r
8049     nowChecked = IDM_EditPosition;\r
8050     break;\r
8051   case Training:\r
8052     nowChecked = IDM_Training;\r
8053     break;\r
8054   case IcsPlayingWhite:\r
8055   case IcsPlayingBlack:\r
8056   case IcsObserving:\r
8057   case IcsIdle:\r
8058     nowChecked = IDM_IcsClient;\r
8059     break;\r
8060   default:\r
8061   case EndOfGame:\r
8062     nowChecked = 0;\r
8063     break;\r
8064   }\r
8065   CheckMark(prevChecked, MF_UNCHECKED);\r
8066   CheckMark(nowChecked, MF_CHECKED);\r
8067   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8068 \r
8069   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8070     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8071                           MF_BYCOMMAND|MF_ENABLED);\r
8072   } else {\r
8073     (void) EnableMenuItem(GetMenu(hwndMain), \r
8074                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8075   }\r
8076 \r
8077   prevChecked = nowChecked;\r
8078 \r
8079   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8080   if (appData.icsActive) {\r
8081        if (appData.icsEngineAnalyze) {\r
8082                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8083        } else {\r
8084                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8085        }\r
8086   }\r
8087   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8088 }\r
8089 \r
8090 VOID\r
8091 SetICSMode()\r
8092 {\r
8093   HMENU hmenu = GetMenu(hwndMain);\r
8094   SetMenuEnables(hmenu, icsEnables);\r
8095   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8096     MF_BYCOMMAND|MF_ENABLED);\r
8097 #if ZIPPY\r
8098   if (appData.zippyPlay) {\r
8099     SetMenuEnables(hmenu, zippyEnables);\r
8100     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8101          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8102           MF_BYCOMMAND|MF_ENABLED);\r
8103   }\r
8104 #endif\r
8105 }\r
8106 \r
8107 VOID\r
8108 SetGNUMode()\r
8109 {\r
8110   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8111 }\r
8112 \r
8113 VOID\r
8114 SetNCPMode()\r
8115 {\r
8116   HMENU hmenu = GetMenu(hwndMain);\r
8117   SetMenuEnables(hmenu, ncpEnables);\r
8118     DrawMenuBar(hwndMain);\r
8119 }\r
8120 \r
8121 VOID\r
8122 SetCmailMode()\r
8123 {\r
8124   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8125 }\r
8126 \r
8127 VOID \r
8128 SetTrainingModeOn()\r
8129 {\r
8130   int i;\r
8131   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8132   for (i = 0; i < N_BUTTONS; i++) {\r
8133     if (buttonDesc[i].hwnd != NULL)\r
8134       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8135   }\r
8136   CommentPopDown();\r
8137 }\r
8138 \r
8139 VOID SetTrainingModeOff()\r
8140 {\r
8141   int i;\r
8142   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8143   for (i = 0; i < N_BUTTONS; i++) {\r
8144     if (buttonDesc[i].hwnd != NULL)\r
8145       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8146   }\r
8147 }\r
8148 \r
8149 \r
8150 VOID\r
8151 SetUserThinkingEnables()\r
8152 {\r
8153   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8154 }\r
8155 \r
8156 VOID\r
8157 SetMachineThinkingEnables()\r
8158 {\r
8159   HMENU hMenu = GetMenu(hwndMain);\r
8160   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8161 \r
8162   SetMenuEnables(hMenu, machineThinkingEnables);\r
8163 \r
8164   if (gameMode == MachinePlaysBlack) {\r
8165     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8166   } else if (gameMode == MachinePlaysWhite) {\r
8167     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8168   } else if (gameMode == TwoMachinesPlay) {\r
8169     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8170   }\r
8171 }\r
8172 \r
8173 \r
8174 VOID\r
8175 DisplayTitle(char *str)\r
8176 {\r
8177   char title[MSG_SIZ], *host;\r
8178   if (str[0] != NULLCHAR) {\r
8179     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8180   } else if (appData.icsActive) {\r
8181     if (appData.icsCommPort[0] != NULLCHAR)\r
8182       host = "ICS";\r
8183     else \r
8184       host = appData.icsHost;\r
8185       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8186   } else if (appData.noChessProgram) {\r
8187     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8188   } else {\r
8189     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8190     strcat(title, ": ");\r
8191     strcat(title, first.tidy);\r
8192   }\r
8193   SetWindowText(hwndMain, title);\r
8194 }\r
8195 \r
8196 \r
8197 VOID\r
8198 DisplayMessage(char *str1, char *str2)\r
8199 {\r
8200   HDC hdc;\r
8201   HFONT oldFont;\r
8202   int remain = MESSAGE_TEXT_MAX - 1;\r
8203   int len;\r
8204 \r
8205   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8206   messageText[0] = NULLCHAR;\r
8207   if (*str1) {\r
8208     len = strlen(str1);\r
8209     if (len > remain) len = remain;\r
8210     strncpy(messageText, str1, len);\r
8211     messageText[len] = NULLCHAR;\r
8212     remain -= len;\r
8213   }\r
8214   if (*str2 && remain >= 2) {\r
8215     if (*str1) {\r
8216       strcat(messageText, "  ");\r
8217       remain -= 2;\r
8218     }\r
8219     len = strlen(str2);\r
8220     if (len > remain) len = remain;\r
8221     strncat(messageText, str2, len);\r
8222   }\r
8223   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8224   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8225 \r
8226   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8227 \r
8228   SAYMACHINEMOVE();\r
8229 \r
8230   hdc = GetDC(hwndMain);\r
8231   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8232   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8233              &messageRect, messageText, strlen(messageText), NULL);\r
8234   (void) SelectObject(hdc, oldFont);\r
8235   (void) ReleaseDC(hwndMain, hdc);\r
8236 }\r
8237 \r
8238 VOID\r
8239 DisplayError(char *str, int error)\r
8240 {\r
8241   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8242   int len;\r
8243 \r
8244   if (error == 0) {\r
8245     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8246   } else {\r
8247     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8248                         NULL, error, LANG_NEUTRAL,\r
8249                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8250     if (len > 0) {\r
8251       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8252     } else {\r
8253       ErrorMap *em = errmap;\r
8254       while (em->err != 0 && em->err != error) em++;\r
8255       if (em->err != 0) {\r
8256         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8257       } else {\r
8258         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8259       }\r
8260     }\r
8261   }\r
8262   \r
8263   ErrorPopUp(_("Error"), buf);\r
8264 }\r
8265 \r
8266 \r
8267 VOID\r
8268 DisplayMoveError(char *str)\r
8269 {\r
8270   fromX = fromY = -1;\r
8271   ClearHighlights();\r
8272   DrawPosition(FALSE, NULL);\r
8273   if (appData.popupMoveErrors) {\r
8274     ErrorPopUp(_("Error"), str);\r
8275   } else {\r
8276     DisplayMessage(str, "");\r
8277     moveErrorMessageUp = TRUE;\r
8278   }\r
8279 }\r
8280 \r
8281 VOID\r
8282 DisplayFatalError(char *str, int error, int exitStatus)\r
8283 {\r
8284   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8285   int len;\r
8286   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8287 \r
8288   if (error != 0) {\r
8289     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8290                         NULL, error, LANG_NEUTRAL,\r
8291                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8292     if (len > 0) {\r
8293       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8294     } else {\r
8295       ErrorMap *em = errmap;\r
8296       while (em->err != 0 && em->err != error) em++;\r
8297       if (em->err != 0) {\r
8298         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8299       } else {\r
8300         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8301       }\r
8302     }\r
8303     str = buf;\r
8304   }\r
8305   if (appData.debugMode) {\r
8306     fprintf(debugFP, "%s: %s\n", label, str);\r
8307   }\r
8308   if (appData.popupExitMessage) {\r
8309     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8310                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8311   }\r
8312   ExitEvent(exitStatus);\r
8313 }\r
8314 \r
8315 \r
8316 VOID\r
8317 DisplayInformation(char *str)\r
8318 {\r
8319   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8320 }\r
8321 \r
8322 \r
8323 VOID\r
8324 DisplayNote(char *str)\r
8325 {\r
8326   ErrorPopUp(_("Note"), str);\r
8327 }\r
8328 \r
8329 \r
8330 typedef struct {\r
8331   char *title, *question, *replyPrefix;\r
8332   ProcRef pr;\r
8333 } QuestionParams;\r
8334 \r
8335 LRESULT CALLBACK\r
8336 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8337 {\r
8338   static QuestionParams *qp;\r
8339   char reply[MSG_SIZ];\r
8340   int len, err;\r
8341 \r
8342   switch (message) {\r
8343   case WM_INITDIALOG:\r
8344     qp = (QuestionParams *) lParam;\r
8345     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8346     Translate(hDlg, DLG_Question);\r
8347     SetWindowText(hDlg, qp->title);\r
8348     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8349     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8350     return FALSE;\r
8351 \r
8352   case WM_COMMAND:\r
8353     switch (LOWORD(wParam)) {\r
8354     case IDOK:\r
8355       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8356       if (*reply) strcat(reply, " ");\r
8357       len = strlen(reply);\r
8358       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8359       strcat(reply, "\n");\r
8360       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8361       EndDialog(hDlg, TRUE);\r
8362       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8363       return TRUE;\r
8364     case IDCANCEL:\r
8365       EndDialog(hDlg, FALSE);\r
8366       return TRUE;\r
8367     default:\r
8368       break;\r
8369     }\r
8370     break;\r
8371   }\r
8372   return FALSE;\r
8373 }\r
8374 \r
8375 VOID\r
8376 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8377 {\r
8378     QuestionParams qp;\r
8379     FARPROC lpProc;\r
8380     \r
8381     qp.title = title;\r
8382     qp.question = question;\r
8383     qp.replyPrefix = replyPrefix;\r
8384     qp.pr = pr;\r
8385     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8386     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8387       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8388     FreeProcInstance(lpProc);\r
8389 }\r
8390 \r
8391 /* [AS] Pick FRC position */\r
8392 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8393 {\r
8394     static int * lpIndexFRC;\r
8395     BOOL index_is_ok;\r
8396     char buf[16];\r
8397 \r
8398     switch( message )\r
8399     {\r
8400     case WM_INITDIALOG:\r
8401         lpIndexFRC = (int *) lParam;\r
8402 \r
8403         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8404         Translate(hDlg, DLG_NewGameFRC);\r
8405 \r
8406         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8407         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8408         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8409         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8410 \r
8411         break;\r
8412 \r
8413     case WM_COMMAND:\r
8414         switch( LOWORD(wParam) ) {\r
8415         case IDOK:\r
8416             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8417             EndDialog( hDlg, 0 );\r
8418             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8419             return TRUE;\r
8420         case IDCANCEL:\r
8421             EndDialog( hDlg, 1 );   \r
8422             return TRUE;\r
8423         case IDC_NFG_Edit:\r
8424             if( HIWORD(wParam) == EN_CHANGE ) {\r
8425                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8426 \r
8427                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8428             }\r
8429             return TRUE;\r
8430         case IDC_NFG_Random:\r
8431           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8432             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8433             return TRUE;\r
8434         }\r
8435 \r
8436         break;\r
8437     }\r
8438 \r
8439     return FALSE;\r
8440 }\r
8441 \r
8442 int NewGameFRC()\r
8443 {\r
8444     int result;\r
8445     int index = appData.defaultFrcPosition;\r
8446     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8447 \r
8448     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8449 \r
8450     if( result == 0 ) {\r
8451         appData.defaultFrcPosition = index;\r
8452     }\r
8453 \r
8454     return result;\r
8455 }\r
8456 \r
8457 /* [AS] Game list options. Refactored by HGM */\r
8458 \r
8459 HWND gameListOptionsDialog;\r
8460 \r
8461 // low-level front-end: clear text edit / list widget\r
8462 void\r
8463 GLT_ClearList()\r
8464 {\r
8465     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8466 }\r
8467 \r
8468 // low-level front-end: clear text edit / list widget\r
8469 void\r
8470 GLT_DeSelectList()\r
8471 {\r
8472     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8473 }\r
8474 \r
8475 // low-level front-end: append line to text edit / list widget\r
8476 void\r
8477 GLT_AddToList( char *name )\r
8478 {\r
8479     if( name != 0 ) {\r
8480             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8481     }\r
8482 }\r
8483 \r
8484 // low-level front-end: get line from text edit / list widget\r
8485 Boolean\r
8486 GLT_GetFromList( int index, char *name )\r
8487 {\r
8488     if( name != 0 ) {\r
8489             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8490                 return TRUE;\r
8491     }\r
8492     return FALSE;\r
8493 }\r
8494 \r
8495 void GLT_MoveSelection( HWND hDlg, int delta )\r
8496 {\r
8497     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8498     int idx2 = idx1 + delta;\r
8499     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8500 \r
8501     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8502         char buf[128];\r
8503 \r
8504         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8505         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8506         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8507         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8508     }\r
8509 }\r
8510 \r
8511 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8512 {\r
8513     switch( message )\r
8514     {\r
8515     case WM_INITDIALOG:\r
8516         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8517         \r
8518         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8519         Translate(hDlg, DLG_GameListOptions);\r
8520 \r
8521         /* Initialize list */\r
8522         GLT_TagsToList( lpUserGLT );\r
8523 \r
8524         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8525 \r
8526         break;\r
8527 \r
8528     case WM_COMMAND:\r
8529         switch( LOWORD(wParam) ) {\r
8530         case IDOK:\r
8531             GLT_ParseList();\r
8532             EndDialog( hDlg, 0 );\r
8533             return TRUE;\r
8534         case IDCANCEL:\r
8535             EndDialog( hDlg, 1 );\r
8536             return TRUE;\r
8537 \r
8538         case IDC_GLT_Default:\r
8539             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8540             return TRUE;\r
8541 \r
8542         case IDC_GLT_Restore:\r
8543             GLT_TagsToList( appData.gameListTags );\r
8544             return TRUE;\r
8545 \r
8546         case IDC_GLT_Up:\r
8547             GLT_MoveSelection( hDlg, -1 );\r
8548             return TRUE;\r
8549 \r
8550         case IDC_GLT_Down:\r
8551             GLT_MoveSelection( hDlg, +1 );\r
8552             return TRUE;\r
8553         }\r
8554 \r
8555         break;\r
8556     }\r
8557 \r
8558     return FALSE;\r
8559 }\r
8560 \r
8561 int GameListOptions()\r
8562 {\r
8563     int result;\r
8564     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8565 \r
8566       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8567 \r
8568     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8569 \r
8570     if( result == 0 ) {\r
8571         /* [AS] Memory leak here! */\r
8572         appData.gameListTags = strdup( lpUserGLT ); \r
8573     }\r
8574 \r
8575     return result;\r
8576 }\r
8577 \r
8578 VOID\r
8579 DisplayIcsInteractionTitle(char *str)\r
8580 {\r
8581   char consoleTitle[MSG_SIZ];\r
8582 \r
8583     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8584     SetWindowText(hwndConsole, consoleTitle);\r
8585 \r
8586     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8587       char buf[MSG_SIZ], *p = buf, *q;\r
8588         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8589       do {\r
8590         q = strchr(p, ';');\r
8591         if(q) *q++ = 0;\r
8592         if(*p) ChatPopUp(p);\r
8593       } while(p=q);\r
8594     }\r
8595 \r
8596     SetActiveWindow(hwndMain);\r
8597 }\r
8598 \r
8599 void\r
8600 DrawPosition(int fullRedraw, Board board)\r
8601 {\r
8602   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8603 }\r
8604 \r
8605 void NotifyFrontendLogin()\r
8606 {\r
8607         if (hwndConsole)\r
8608                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8609 }\r
8610 \r
8611 VOID\r
8612 ResetFrontEnd()\r
8613 {\r
8614   fromX = fromY = -1;\r
8615   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8616     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8617     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8618     dragInfo.lastpos = dragInfo.pos;\r
8619     dragInfo.start.x = dragInfo.start.y = -1;\r
8620     dragInfo.from = dragInfo.start;\r
8621     ReleaseCapture();\r
8622     DrawPosition(TRUE, NULL);\r
8623   }\r
8624   TagsPopDown();\r
8625 }\r
8626 \r
8627 \r
8628 VOID\r
8629 CommentPopUp(char *title, char *str)\r
8630 {\r
8631   HWND hwnd = GetActiveWindow();\r
8632   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8633   SAY(str);\r
8634   SetActiveWindow(hwnd);\r
8635 }\r
8636 \r
8637 VOID\r
8638 CommentPopDown(void)\r
8639 {\r
8640   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8641   if (commentDialog) {\r
8642     ShowWindow(commentDialog, SW_HIDE);\r
8643   }\r
8644   commentUp = FALSE;\r
8645 }\r
8646 \r
8647 VOID\r
8648 EditCommentPopUp(int index, char *title, char *str)\r
8649 {\r
8650   EitherCommentPopUp(index, title, str, TRUE);\r
8651 }\r
8652 \r
8653 \r
8654 VOID\r
8655 RingBell()\r
8656 {\r
8657   MyPlaySound(&sounds[(int)SoundMove]);\r
8658 }\r
8659 \r
8660 VOID PlayIcsWinSound()\r
8661 {\r
8662   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8663 }\r
8664 \r
8665 VOID PlayIcsLossSound()\r
8666 {\r
8667   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8668 }\r
8669 \r
8670 VOID PlayIcsDrawSound()\r
8671 {\r
8672   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8673 }\r
8674 \r
8675 VOID PlayIcsUnfinishedSound()\r
8676 {\r
8677   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8678 }\r
8679 \r
8680 VOID\r
8681 PlayAlarmSound()\r
8682 {\r
8683   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8684 }\r
8685 \r
8686 VOID\r
8687 PlayTellSound()\r
8688 {\r
8689   MyPlaySound(&textAttribs[ColorTell].sound);\r
8690 }\r
8691 \r
8692 \r
8693 VOID\r
8694 EchoOn()\r
8695 {\r
8696   HWND hInput;\r
8697   consoleEcho = TRUE;\r
8698   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8699   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8700   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8701 }\r
8702 \r
8703 \r
8704 VOID\r
8705 EchoOff()\r
8706 {\r
8707   CHARFORMAT cf;\r
8708   HWND hInput;\r
8709   consoleEcho = FALSE;\r
8710   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8711   /* This works OK: set text and background both to the same color */\r
8712   cf = consoleCF;\r
8713   cf.crTextColor = COLOR_ECHOOFF;\r
8714   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8715   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8716 }\r
8717 \r
8718 /* No Raw()...? */\r
8719 \r
8720 void Colorize(ColorClass cc, int continuation)\r
8721 {\r
8722   currentColorClass = cc;\r
8723   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8724   consoleCF.crTextColor = textAttribs[cc].color;\r
8725   consoleCF.dwEffects = textAttribs[cc].effects;\r
8726   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8727 }\r
8728 \r
8729 char *\r
8730 UserName()\r
8731 {\r
8732   static char buf[MSG_SIZ];\r
8733   DWORD bufsiz = MSG_SIZ;\r
8734 \r
8735   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8736         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8737   }\r
8738   if (!GetUserName(buf, &bufsiz)) {\r
8739     /*DisplayError("Error getting user name", GetLastError());*/\r
8740     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8741   }\r
8742   return buf;\r
8743 }\r
8744 \r
8745 char *\r
8746 HostName()\r
8747 {\r
8748   static char buf[MSG_SIZ];\r
8749   DWORD bufsiz = MSG_SIZ;\r
8750 \r
8751   if (!GetComputerName(buf, &bufsiz)) {\r
8752     /*DisplayError("Error getting host name", GetLastError());*/\r
8753     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8754   }\r
8755   return buf;\r
8756 }\r
8757 \r
8758 \r
8759 int\r
8760 ClockTimerRunning()\r
8761 {\r
8762   return clockTimerEvent != 0;\r
8763 }\r
8764 \r
8765 int\r
8766 StopClockTimer()\r
8767 {\r
8768   if (clockTimerEvent == 0) return FALSE;\r
8769   KillTimer(hwndMain, clockTimerEvent);\r
8770   clockTimerEvent = 0;\r
8771   return TRUE;\r
8772 }\r
8773 \r
8774 void\r
8775 StartClockTimer(long millisec)\r
8776 {\r
8777   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8778                              (UINT) millisec, NULL);\r
8779 }\r
8780 \r
8781 void\r
8782 DisplayWhiteClock(long timeRemaining, int highlight)\r
8783 {\r
8784   HDC hdc;\r
8785   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8786 \r
8787   if(appData.noGUI) return;\r
8788   hdc = GetDC(hwndMain);\r
8789   if (!IsIconic(hwndMain)) {\r
8790     DisplayAClock(hdc, timeRemaining, highlight, \r
8791                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8792   }\r
8793   if (highlight && iconCurrent == iconBlack) {\r
8794     iconCurrent = iconWhite;\r
8795     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8796     if (IsIconic(hwndMain)) {\r
8797       DrawIcon(hdc, 2, 2, iconCurrent);\r
8798     }\r
8799   }\r
8800   (void) ReleaseDC(hwndMain, hdc);\r
8801   if (hwndConsole)\r
8802     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8803 }\r
8804 \r
8805 void\r
8806 DisplayBlackClock(long timeRemaining, int highlight)\r
8807 {\r
8808   HDC hdc;\r
8809   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8810 \r
8811   if(appData.noGUI) return;\r
8812   hdc = GetDC(hwndMain);\r
8813   if (!IsIconic(hwndMain)) {\r
8814     DisplayAClock(hdc, timeRemaining, highlight, \r
8815                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8816   }\r
8817   if (highlight && iconCurrent == iconWhite) {\r
8818     iconCurrent = iconBlack;\r
8819     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8820     if (IsIconic(hwndMain)) {\r
8821       DrawIcon(hdc, 2, 2, iconCurrent);\r
8822     }\r
8823   }\r
8824   (void) ReleaseDC(hwndMain, hdc);\r
8825   if (hwndConsole)\r
8826     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8827 }\r
8828 \r
8829 \r
8830 int\r
8831 LoadGameTimerRunning()\r
8832 {\r
8833   return loadGameTimerEvent != 0;\r
8834 }\r
8835 \r
8836 int\r
8837 StopLoadGameTimer()\r
8838 {\r
8839   if (loadGameTimerEvent == 0) return FALSE;\r
8840   KillTimer(hwndMain, loadGameTimerEvent);\r
8841   loadGameTimerEvent = 0;\r
8842   return TRUE;\r
8843 }\r
8844 \r
8845 void\r
8846 StartLoadGameTimer(long millisec)\r
8847 {\r
8848   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8849                                 (UINT) millisec, NULL);\r
8850 }\r
8851 \r
8852 void\r
8853 AutoSaveGame()\r
8854 {\r
8855   char *defName;\r
8856   FILE *f;\r
8857   char fileTitle[MSG_SIZ];\r
8858 \r
8859   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8860   f = OpenFileDialog(hwndMain, "a", defName,\r
8861                      appData.oldSaveStyle ? "gam" : "pgn",\r
8862                      GAME_FILT, \r
8863                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8864   if (f != NULL) {\r
8865     SaveGame(f, 0, "");\r
8866     fclose(f);\r
8867   }\r
8868 }\r
8869 \r
8870 \r
8871 void\r
8872 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8873 {\r
8874   if (delayedTimerEvent != 0) {\r
8875     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8876       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8877     }\r
8878     KillTimer(hwndMain, delayedTimerEvent);\r
8879     delayedTimerEvent = 0;\r
8880     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8881     delayedTimerCallback();\r
8882   }\r
8883   delayedTimerCallback = cb;\r
8884   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8885                                 (UINT) millisec, NULL);\r
8886 }\r
8887 \r
8888 DelayedEventCallback\r
8889 GetDelayedEvent()\r
8890 {\r
8891   if (delayedTimerEvent) {\r
8892     return delayedTimerCallback;\r
8893   } else {\r
8894     return NULL;\r
8895   }\r
8896 }\r
8897 \r
8898 void\r
8899 CancelDelayedEvent()\r
8900 {\r
8901   if (delayedTimerEvent) {\r
8902     KillTimer(hwndMain, delayedTimerEvent);\r
8903     delayedTimerEvent = 0;\r
8904   }\r
8905 }\r
8906 \r
8907 DWORD GetWin32Priority(int nice)\r
8908 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8909 /*\r
8910 REALTIME_PRIORITY_CLASS     0x00000100\r
8911 HIGH_PRIORITY_CLASS         0x00000080\r
8912 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8913 NORMAL_PRIORITY_CLASS       0x00000020\r
8914 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8915 IDLE_PRIORITY_CLASS         0x00000040\r
8916 */\r
8917         if (nice < -15) return 0x00000080;\r
8918         if (nice < 0)   return 0x00008000;\r
8919         if (nice == 0)  return 0x00000020;\r
8920         if (nice < 15)  return 0x00004000;\r
8921         return 0x00000040;\r
8922 }\r
8923 \r
8924 void RunCommand(char *cmdLine)\r
8925 {\r
8926   /* Now create the child process. */\r
8927   STARTUPINFO siStartInfo;\r
8928   PROCESS_INFORMATION piProcInfo;\r
8929 \r
8930   siStartInfo.cb = sizeof(STARTUPINFO);\r
8931   siStartInfo.lpReserved = NULL;\r
8932   siStartInfo.lpDesktop = NULL;\r
8933   siStartInfo.lpTitle = NULL;\r
8934   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8935   siStartInfo.cbReserved2 = 0;\r
8936   siStartInfo.lpReserved2 = NULL;\r
8937   siStartInfo.hStdInput = NULL;\r
8938   siStartInfo.hStdOutput = NULL;\r
8939   siStartInfo.hStdError = NULL;\r
8940 \r
8941   CreateProcess(NULL,\r
8942                 cmdLine,           /* command line */\r
8943                 NULL,      /* process security attributes */\r
8944                 NULL,      /* primary thread security attrs */\r
8945                 TRUE,      /* handles are inherited */\r
8946                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8947                 NULL,      /* use parent's environment */\r
8948                 NULL,\r
8949                 &siStartInfo, /* STARTUPINFO pointer */\r
8950                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8951 \r
8952   CloseHandle(piProcInfo.hThread);\r
8953 }\r
8954 \r
8955 /* Start a child process running the given program.\r
8956    The process's standard output can be read from "from", and its\r
8957    standard input can be written to "to".\r
8958    Exit with fatal error if anything goes wrong.\r
8959    Returns an opaque pointer that can be used to destroy the process\r
8960    later.\r
8961 */\r
8962 int\r
8963 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8964 {\r
8965 #define BUFSIZE 4096\r
8966 \r
8967   HANDLE hChildStdinRd, hChildStdinWr,\r
8968     hChildStdoutRd, hChildStdoutWr;\r
8969   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8970   SECURITY_ATTRIBUTES saAttr;\r
8971   BOOL fSuccess;\r
8972   PROCESS_INFORMATION piProcInfo;\r
8973   STARTUPINFO siStartInfo;\r
8974   ChildProc *cp;\r
8975   char buf[MSG_SIZ];\r
8976   DWORD err;\r
8977 \r
8978   if (appData.debugMode) {\r
8979     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8980   }\r
8981 \r
8982   *pr = NoProc;\r
8983 \r
8984   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8985   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8986   saAttr.bInheritHandle = TRUE;\r
8987   saAttr.lpSecurityDescriptor = NULL;\r
8988 \r
8989   /*\r
8990    * The steps for redirecting child's STDOUT:\r
8991    *     1. Create anonymous pipe to be STDOUT for child.\r
8992    *     2. Create a noninheritable duplicate of read handle,\r
8993    *         and close the inheritable read handle.\r
8994    */\r
8995 \r
8996   /* Create a pipe for the child's STDOUT. */\r
8997   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8998     return GetLastError();\r
8999   }\r
9000 \r
9001   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9002   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9003                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9004                              FALSE,     /* not inherited */\r
9005                              DUPLICATE_SAME_ACCESS);\r
9006   if (! fSuccess) {\r
9007     return GetLastError();\r
9008   }\r
9009   CloseHandle(hChildStdoutRd);\r
9010 \r
9011   /*\r
9012    * The steps for redirecting child's STDIN:\r
9013    *     1. Create anonymous pipe to be STDIN for child.\r
9014    *     2. Create a noninheritable duplicate of write handle,\r
9015    *         and close the inheritable write handle.\r
9016    */\r
9017 \r
9018   /* Create a pipe for the child's STDIN. */\r
9019   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9020     return GetLastError();\r
9021   }\r
9022 \r
9023   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9024   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9025                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9026                              FALSE,     /* not inherited */\r
9027                              DUPLICATE_SAME_ACCESS);\r
9028   if (! fSuccess) {\r
9029     return GetLastError();\r
9030   }\r
9031   CloseHandle(hChildStdinWr);\r
9032 \r
9033   /* Arrange to (1) look in dir for the child .exe file, and\r
9034    * (2) have dir be the child's working directory.  Interpret\r
9035    * dir relative to the directory WinBoard loaded from. */\r
9036   GetCurrentDirectory(MSG_SIZ, buf);\r
9037   SetCurrentDirectory(installDir);\r
9038   SetCurrentDirectory(dir);\r
9039 \r
9040   /* Now create the child process. */\r
9041 \r
9042   siStartInfo.cb = sizeof(STARTUPINFO);\r
9043   siStartInfo.lpReserved = NULL;\r
9044   siStartInfo.lpDesktop = NULL;\r
9045   siStartInfo.lpTitle = NULL;\r
9046   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9047   siStartInfo.cbReserved2 = 0;\r
9048   siStartInfo.lpReserved2 = NULL;\r
9049   siStartInfo.hStdInput = hChildStdinRd;\r
9050   siStartInfo.hStdOutput = hChildStdoutWr;\r
9051   siStartInfo.hStdError = hChildStdoutWr;\r
9052 \r
9053   fSuccess = CreateProcess(NULL,\r
9054                            cmdLine,        /* command line */\r
9055                            NULL,           /* process security attributes */\r
9056                            NULL,           /* primary thread security attrs */\r
9057                            TRUE,           /* handles are inherited */\r
9058                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9059                            NULL,           /* use parent's environment */\r
9060                            NULL,\r
9061                            &siStartInfo, /* STARTUPINFO pointer */\r
9062                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9063 \r
9064   err = GetLastError();\r
9065   SetCurrentDirectory(buf); /* return to prev directory */\r
9066   if (! fSuccess) {\r
9067     return err;\r
9068   }\r
9069 \r
9070   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9071     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9072     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9073   }\r
9074 \r
9075   /* Close the handles we don't need in the parent */\r
9076   CloseHandle(piProcInfo.hThread);\r
9077   CloseHandle(hChildStdinRd);\r
9078   CloseHandle(hChildStdoutWr);\r
9079 \r
9080   /* Prepare return value */\r
9081   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9082   cp->kind = CPReal;\r
9083   cp->hProcess = piProcInfo.hProcess;\r
9084   cp->pid = piProcInfo.dwProcessId;\r
9085   cp->hFrom = hChildStdoutRdDup;\r
9086   cp->hTo = hChildStdinWrDup;\r
9087 \r
9088   *pr = (void *) cp;\r
9089 \r
9090   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9091      2000 where engines sometimes don't see the initial command(s)\r
9092      from WinBoard and hang.  I don't understand how that can happen,\r
9093      but the Sleep is harmless, so I've put it in.  Others have also\r
9094      reported what may be the same problem, so hopefully this will fix\r
9095      it for them too.  */\r
9096   Sleep(500);\r
9097 \r
9098   return NO_ERROR;\r
9099 }\r
9100 \r
9101 \r
9102 void\r
9103 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9104 {\r
9105   ChildProc *cp; int result;\r
9106 \r
9107   cp = (ChildProc *) pr;\r
9108   if (cp == NULL) return;\r
9109 \r
9110   switch (cp->kind) {\r
9111   case CPReal:\r
9112     /* TerminateProcess is considered harmful, so... */\r
9113     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9114     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9115     /* The following doesn't work because the chess program\r
9116        doesn't "have the same console" as WinBoard.  Maybe\r
9117        we could arrange for this even though neither WinBoard\r
9118        nor the chess program uses a console for stdio? */\r
9119     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9120 \r
9121     /* [AS] Special termination modes for misbehaving programs... */\r
9122     if( signal == 9 ) { \r
9123         result = TerminateProcess( cp->hProcess, 0 );\r
9124 \r
9125         if ( appData.debugMode) {\r
9126             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9127         }\r
9128     }\r
9129     else if( signal == 10 ) {\r
9130         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9131 \r
9132         if( dw != WAIT_OBJECT_0 ) {\r
9133             result = TerminateProcess( cp->hProcess, 0 );\r
9134 \r
9135             if ( appData.debugMode) {\r
9136                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9137             }\r
9138 \r
9139         }\r
9140     }\r
9141 \r
9142     CloseHandle(cp->hProcess);\r
9143     break;\r
9144 \r
9145   case CPComm:\r
9146     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9147     break;\r
9148 \r
9149   case CPSock:\r
9150     closesocket(cp->sock);\r
9151     WSACleanup();\r
9152     break;\r
9153 \r
9154   case CPRcmd:\r
9155     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9156     closesocket(cp->sock);\r
9157     closesocket(cp->sock2);\r
9158     WSACleanup();\r
9159     break;\r
9160   }\r
9161   free(cp);\r
9162 }\r
9163 \r
9164 void\r
9165 InterruptChildProcess(ProcRef pr)\r
9166 {\r
9167   ChildProc *cp;\r
9168 \r
9169   cp = (ChildProc *) pr;\r
9170   if (cp == NULL) return;\r
9171   switch (cp->kind) {\r
9172   case CPReal:\r
9173     /* The following doesn't work because the chess program\r
9174        doesn't "have the same console" as WinBoard.  Maybe\r
9175        we could arrange for this even though neither WinBoard\r
9176        nor the chess program uses a console for stdio */\r
9177     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9178     break;\r
9179 \r
9180   case CPComm:\r
9181   case CPSock:\r
9182     /* Can't interrupt */\r
9183     break;\r
9184 \r
9185   case CPRcmd:\r
9186     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9187     break;\r
9188   }\r
9189 }\r
9190 \r
9191 \r
9192 int\r
9193 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9194 {\r
9195   char cmdLine[MSG_SIZ];\r
9196 \r
9197   if (port[0] == NULLCHAR) {\r
9198     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9199   } else {\r
9200     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9201   }\r
9202   return StartChildProcess(cmdLine, "", pr);\r
9203 }\r
9204 \r
9205 \r
9206 /* Code to open TCP sockets */\r
9207 \r
9208 int\r
9209 OpenTCP(char *host, char *port, ProcRef *pr)\r
9210 {\r
9211   ChildProc *cp;\r
9212   int err;\r
9213   SOCKET s;\r
9214 \r
9215   struct sockaddr_in sa, mysa;\r
9216   struct hostent FAR *hp;\r
9217   unsigned short uport;\r
9218   WORD wVersionRequested;\r
9219   WSADATA wsaData;\r
9220 \r
9221   /* Initialize socket DLL */\r
9222   wVersionRequested = MAKEWORD(1, 1);\r
9223   err = WSAStartup(wVersionRequested, &wsaData);\r
9224   if (err != 0) return err;\r
9225 \r
9226   /* Make socket */\r
9227   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9228     err = WSAGetLastError();\r
9229     WSACleanup();\r
9230     return err;\r
9231   }\r
9232 \r
9233   /* Bind local address using (mostly) don't-care values.\r
9234    */\r
9235   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9236   mysa.sin_family = AF_INET;\r
9237   mysa.sin_addr.s_addr = INADDR_ANY;\r
9238   uport = (unsigned short) 0;\r
9239   mysa.sin_port = htons(uport);\r
9240   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9241       == SOCKET_ERROR) {\r
9242     err = WSAGetLastError();\r
9243     WSACleanup();\r
9244     return err;\r
9245   }\r
9246 \r
9247   /* Resolve remote host name */\r
9248   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9249   if (!(hp = gethostbyname(host))) {\r
9250     unsigned int b0, b1, b2, b3;\r
9251 \r
9252     err = WSAGetLastError();\r
9253 \r
9254     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9255       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9256       hp->h_addrtype = AF_INET;\r
9257       hp->h_length = 4;\r
9258       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9259       hp->h_addr_list[0] = (char *) malloc(4);\r
9260       hp->h_addr_list[0][0] = (char) b0;\r
9261       hp->h_addr_list[0][1] = (char) b1;\r
9262       hp->h_addr_list[0][2] = (char) b2;\r
9263       hp->h_addr_list[0][3] = (char) b3;\r
9264     } else {\r
9265       WSACleanup();\r
9266       return err;\r
9267     }\r
9268   }\r
9269   sa.sin_family = hp->h_addrtype;\r
9270   uport = (unsigned short) atoi(port);\r
9271   sa.sin_port = htons(uport);\r
9272   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9273 \r
9274   /* Make connection */\r
9275   if (connect(s, (struct sockaddr *) &sa,\r
9276               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9277     err = WSAGetLastError();\r
9278     WSACleanup();\r
9279     return err;\r
9280   }\r
9281 \r
9282   /* Prepare return value */\r
9283   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9284   cp->kind = CPSock;\r
9285   cp->sock = s;\r
9286   *pr = (ProcRef *) cp;\r
9287 \r
9288   return NO_ERROR;\r
9289 }\r
9290 \r
9291 int\r
9292 OpenCommPort(char *name, ProcRef *pr)\r
9293 {\r
9294   HANDLE h;\r
9295   COMMTIMEOUTS ct;\r
9296   ChildProc *cp;\r
9297   char fullname[MSG_SIZ];\r
9298 \r
9299   if (*name != '\\')\r
9300     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9301   else\r
9302     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9303 \r
9304   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9305                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9306   if (h == (HANDLE) -1) {\r
9307     return GetLastError();\r
9308   }\r
9309   hCommPort = h;\r
9310 \r
9311   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9312 \r
9313   /* Accumulate characters until a 100ms pause, then parse */\r
9314   ct.ReadIntervalTimeout = 100;\r
9315   ct.ReadTotalTimeoutMultiplier = 0;\r
9316   ct.ReadTotalTimeoutConstant = 0;\r
9317   ct.WriteTotalTimeoutMultiplier = 0;\r
9318   ct.WriteTotalTimeoutConstant = 0;\r
9319   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9320 \r
9321   /* Prepare return value */\r
9322   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9323   cp->kind = CPComm;\r
9324   cp->hFrom = h;\r
9325   cp->hTo = h;\r
9326   *pr = (ProcRef *) cp;\r
9327 \r
9328   return NO_ERROR;\r
9329 }\r
9330 \r
9331 int\r
9332 OpenLoopback(ProcRef *pr)\r
9333 {\r
9334   DisplayFatalError(_("Not implemented"), 0, 1);\r
9335   return NO_ERROR;\r
9336 }\r
9337 \r
9338 \r
9339 int\r
9340 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9341 {\r
9342   ChildProc *cp;\r
9343   int err;\r
9344   SOCKET s, s2, s3;\r
9345   struct sockaddr_in sa, mysa;\r
9346   struct hostent FAR *hp;\r
9347   unsigned short uport;\r
9348   WORD wVersionRequested;\r
9349   WSADATA wsaData;\r
9350   int fromPort;\r
9351   char stderrPortStr[MSG_SIZ];\r
9352 \r
9353   /* Initialize socket DLL */\r
9354   wVersionRequested = MAKEWORD(1, 1);\r
9355   err = WSAStartup(wVersionRequested, &wsaData);\r
9356   if (err != 0) return err;\r
9357 \r
9358   /* Resolve remote host name */\r
9359   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9360   if (!(hp = gethostbyname(host))) {\r
9361     unsigned int b0, b1, b2, b3;\r
9362 \r
9363     err = WSAGetLastError();\r
9364 \r
9365     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9366       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9367       hp->h_addrtype = AF_INET;\r
9368       hp->h_length = 4;\r
9369       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9370       hp->h_addr_list[0] = (char *) malloc(4);\r
9371       hp->h_addr_list[0][0] = (char) b0;\r
9372       hp->h_addr_list[0][1] = (char) b1;\r
9373       hp->h_addr_list[0][2] = (char) b2;\r
9374       hp->h_addr_list[0][3] = (char) b3;\r
9375     } else {\r
9376       WSACleanup();\r
9377       return err;\r
9378     }\r
9379   }\r
9380   sa.sin_family = hp->h_addrtype;\r
9381   uport = (unsigned short) 514;\r
9382   sa.sin_port = htons(uport);\r
9383   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9384 \r
9385   /* Bind local socket to unused "privileged" port address\r
9386    */\r
9387   s = INVALID_SOCKET;\r
9388   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9389   mysa.sin_family = AF_INET;\r
9390   mysa.sin_addr.s_addr = INADDR_ANY;\r
9391   for (fromPort = 1023;; fromPort--) {\r
9392     if (fromPort < 0) {\r
9393       WSACleanup();\r
9394       return WSAEADDRINUSE;\r
9395     }\r
9396     if (s == INVALID_SOCKET) {\r
9397       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9398         err = WSAGetLastError();\r
9399         WSACleanup();\r
9400         return err;\r
9401       }\r
9402     }\r
9403     uport = (unsigned short) fromPort;\r
9404     mysa.sin_port = htons(uport);\r
9405     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9406         == SOCKET_ERROR) {\r
9407       err = WSAGetLastError();\r
9408       if (err == WSAEADDRINUSE) continue;\r
9409       WSACleanup();\r
9410       return err;\r
9411     }\r
9412     if (connect(s, (struct sockaddr *) &sa,\r
9413       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9414       err = WSAGetLastError();\r
9415       if (err == WSAEADDRINUSE) {\r
9416         closesocket(s);\r
9417         s = -1;\r
9418         continue;\r
9419       }\r
9420       WSACleanup();\r
9421       return err;\r
9422     }\r
9423     break;\r
9424   }\r
9425 \r
9426   /* Bind stderr local socket to unused "privileged" port address\r
9427    */\r
9428   s2 = INVALID_SOCKET;\r
9429   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9430   mysa.sin_family = AF_INET;\r
9431   mysa.sin_addr.s_addr = INADDR_ANY;\r
9432   for (fromPort = 1023;; fromPort--) {\r
9433     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9434     if (fromPort < 0) {\r
9435       (void) closesocket(s);\r
9436       WSACleanup();\r
9437       return WSAEADDRINUSE;\r
9438     }\r
9439     if (s2 == INVALID_SOCKET) {\r
9440       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9441         err = WSAGetLastError();\r
9442         closesocket(s);\r
9443         WSACleanup();\r
9444         return err;\r
9445       }\r
9446     }\r
9447     uport = (unsigned short) fromPort;\r
9448     mysa.sin_port = htons(uport);\r
9449     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9450         == SOCKET_ERROR) {\r
9451       err = WSAGetLastError();\r
9452       if (err == WSAEADDRINUSE) continue;\r
9453       (void) closesocket(s);\r
9454       WSACleanup();\r
9455       return err;\r
9456     }\r
9457     if (listen(s2, 1) == SOCKET_ERROR) {\r
9458       err = WSAGetLastError();\r
9459       if (err == WSAEADDRINUSE) {\r
9460         closesocket(s2);\r
9461         s2 = INVALID_SOCKET;\r
9462         continue;\r
9463       }\r
9464       (void) closesocket(s);\r
9465       (void) closesocket(s2);\r
9466       WSACleanup();\r
9467       return err;\r
9468     }\r
9469     break;\r
9470   }\r
9471   prevStderrPort = fromPort; // remember port used\r
9472   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9473 \r
9474   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9475     err = WSAGetLastError();\r
9476     (void) closesocket(s);\r
9477     (void) closesocket(s2);\r
9478     WSACleanup();\r
9479     return err;\r
9480   }\r
9481 \r
9482   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9483     err = WSAGetLastError();\r
9484     (void) closesocket(s);\r
9485     (void) closesocket(s2);\r
9486     WSACleanup();\r
9487     return err;\r
9488   }\r
9489   if (*user == NULLCHAR) user = UserName();\r
9490   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9491     err = WSAGetLastError();\r
9492     (void) closesocket(s);\r
9493     (void) closesocket(s2);\r
9494     WSACleanup();\r
9495     return err;\r
9496   }\r
9497   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9498     err = WSAGetLastError();\r
9499     (void) closesocket(s);\r
9500     (void) closesocket(s2);\r
9501     WSACleanup();\r
9502     return err;\r
9503   }\r
9504 \r
9505   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9506     err = WSAGetLastError();\r
9507     (void) closesocket(s);\r
9508     (void) closesocket(s2);\r
9509     WSACleanup();\r
9510     return err;\r
9511   }\r
9512   (void) closesocket(s2);  /* Stop listening */\r
9513 \r
9514   /* Prepare return value */\r
9515   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9516   cp->kind = CPRcmd;\r
9517   cp->sock = s;\r
9518   cp->sock2 = s3;\r
9519   *pr = (ProcRef *) cp;\r
9520 \r
9521   return NO_ERROR;\r
9522 }\r
9523 \r
9524 \r
9525 InputSourceRef\r
9526 AddInputSource(ProcRef pr, int lineByLine,\r
9527                InputCallback func, VOIDSTAR closure)\r
9528 {\r
9529   InputSource *is, *is2 = NULL;\r
9530   ChildProc *cp = (ChildProc *) pr;\r
9531 \r
9532   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9533   is->lineByLine = lineByLine;\r
9534   is->func = func;\r
9535   is->closure = closure;\r
9536   is->second = NULL;\r
9537   is->next = is->buf;\r
9538   if (pr == NoProc) {\r
9539     is->kind = CPReal;\r
9540     consoleInputSource = is;\r
9541   } else {\r
9542     is->kind = cp->kind;\r
9543     /* \r
9544         [AS] Try to avoid a race condition if the thread is given control too early:\r
9545         we create all threads suspended so that the is->hThread variable can be\r
9546         safely assigned, then let the threads start with ResumeThread.\r
9547     */\r
9548     switch (cp->kind) {\r
9549     case CPReal:\r
9550       is->hFile = cp->hFrom;\r
9551       cp->hFrom = NULL; /* now owned by InputThread */\r
9552       is->hThread =\r
9553         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9554                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9555       break;\r
9556 \r
9557     case CPComm:\r
9558       is->hFile = cp->hFrom;\r
9559       cp->hFrom = NULL; /* now owned by InputThread */\r
9560       is->hThread =\r
9561         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9562                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9563       break;\r
9564 \r
9565     case CPSock:\r
9566       is->sock = cp->sock;\r
9567       is->hThread =\r
9568         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9569                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9570       break;\r
9571 \r
9572     case CPRcmd:\r
9573       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9574       *is2 = *is;\r
9575       is->sock = cp->sock;\r
9576       is->second = is2;\r
9577       is2->sock = cp->sock2;\r
9578       is2->second = is2;\r
9579       is->hThread =\r
9580         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9581                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9582       is2->hThread =\r
9583         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9584                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9585       break;\r
9586     }\r
9587 \r
9588     if( is->hThread != NULL ) {\r
9589         ResumeThread( is->hThread );\r
9590     }\r
9591 \r
9592     if( is2 != NULL && is2->hThread != NULL ) {\r
9593         ResumeThread( is2->hThread );\r
9594     }\r
9595   }\r
9596 \r
9597   return (InputSourceRef) is;\r
9598 }\r
9599 \r
9600 void\r
9601 RemoveInputSource(InputSourceRef isr)\r
9602 {\r
9603   InputSource *is;\r
9604 \r
9605   is = (InputSource *) isr;\r
9606   is->hThread = NULL;  /* tell thread to stop */\r
9607   CloseHandle(is->hThread);\r
9608   if (is->second != NULL) {\r
9609     is->second->hThread = NULL;\r
9610     CloseHandle(is->second->hThread);\r
9611   }\r
9612 }\r
9613 \r
9614 int no_wrap(char *message, int count)\r
9615 {\r
9616     ConsoleOutput(message, count, FALSE);\r
9617     return count;\r
9618 }\r
9619 \r
9620 int\r
9621 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9622 {\r
9623   DWORD dOutCount;\r
9624   int outCount = SOCKET_ERROR;\r
9625   ChildProc *cp = (ChildProc *) pr;\r
9626   static OVERLAPPED ovl;\r
9627   static int line = 0;\r
9628 \r
9629   if (pr == NoProc)\r
9630   {\r
9631     if (appData.noJoin || !appData.useInternalWrap)\r
9632       return no_wrap(message, count);\r
9633     else\r
9634     {\r
9635       int width = get_term_width();\r
9636       int len = wrap(NULL, message, count, width, &line);\r
9637       char *msg = malloc(len);\r
9638       int dbgchk;\r
9639 \r
9640       if (!msg)\r
9641         return no_wrap(message, count);\r
9642       else\r
9643       {\r
9644         dbgchk = wrap(msg, message, count, width, &line);\r
9645         if (dbgchk != len && appData.debugMode)\r
9646             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9647         ConsoleOutput(msg, len, FALSE);\r
9648         free(msg);\r
9649         return len;\r
9650       }\r
9651     }\r
9652   }\r
9653 \r
9654   if (ovl.hEvent == NULL) {\r
9655     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9656   }\r
9657   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9658 \r
9659   switch (cp->kind) {\r
9660   case CPSock:\r
9661   case CPRcmd:\r
9662     outCount = send(cp->sock, message, count, 0);\r
9663     if (outCount == SOCKET_ERROR) {\r
9664       *outError = WSAGetLastError();\r
9665     } else {\r
9666       *outError = NO_ERROR;\r
9667     }\r
9668     break;\r
9669 \r
9670   case CPReal:\r
9671     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9672                   &dOutCount, NULL)) {\r
9673       *outError = NO_ERROR;\r
9674       outCount = (int) dOutCount;\r
9675     } else {\r
9676       *outError = GetLastError();\r
9677     }\r
9678     break;\r
9679 \r
9680   case CPComm:\r
9681     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9682                             &dOutCount, &ovl);\r
9683     if (*outError == NO_ERROR) {\r
9684       outCount = (int) dOutCount;\r
9685     }\r
9686     break;\r
9687   }\r
9688   return outCount;\r
9689 }\r
9690 \r
9691 void\r
9692 DoSleep(int n)\r
9693 {\r
9694     if(n != 0) Sleep(n);\r
9695 }\r
9696 \r
9697 int\r
9698 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9699                        long msdelay)\r
9700 {\r
9701   /* Ignore delay, not implemented for WinBoard */\r
9702   return OutputToProcess(pr, message, count, outError);\r
9703 }\r
9704 \r
9705 \r
9706 void\r
9707 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9708                         char *buf, int count, int error)\r
9709 {\r
9710   DisplayFatalError(_("Not implemented"), 0, 1);\r
9711 }\r
9712 \r
9713 /* see wgamelist.c for Game List functions */\r
9714 /* see wedittags.c for Edit Tags functions */\r
9715 \r
9716 \r
9717 VOID\r
9718 ICSInitScript()\r
9719 {\r
9720   FILE *f;\r
9721   char buf[MSG_SIZ];\r
9722   char *dummy;\r
9723 \r
9724   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9725     f = fopen(buf, "r");\r
9726     if (f != NULL) {\r
9727       ProcessICSInitScript(f);\r
9728       fclose(f);\r
9729     }\r
9730   }\r
9731 }\r
9732 \r
9733 \r
9734 VOID\r
9735 StartAnalysisClock()\r
9736 {\r
9737   if (analysisTimerEvent) return;\r
9738   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9739                                         (UINT) 2000, NULL);\r
9740 }\r
9741 \r
9742 VOID\r
9743 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9744 {\r
9745   highlightInfo.sq[0].x = fromX;\r
9746   highlightInfo.sq[0].y = fromY;\r
9747   highlightInfo.sq[1].x = toX;\r
9748   highlightInfo.sq[1].y = toY;\r
9749 }\r
9750 \r
9751 VOID\r
9752 ClearHighlights()\r
9753 {\r
9754   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9755     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9756 }\r
9757 \r
9758 VOID\r
9759 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9760 {\r
9761   premoveHighlightInfo.sq[0].x = fromX;\r
9762   premoveHighlightInfo.sq[0].y = fromY;\r
9763   premoveHighlightInfo.sq[1].x = toX;\r
9764   premoveHighlightInfo.sq[1].y = toY;\r
9765 }\r
9766 \r
9767 VOID\r
9768 ClearPremoveHighlights()\r
9769 {\r
9770   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9771     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9772 }\r
9773 \r
9774 VOID\r
9775 ShutDownFrontEnd()\r
9776 {\r
9777   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9778   DeleteClipboardTempFiles();\r
9779 }\r
9780 \r
9781 void\r
9782 BoardToTop()\r
9783 {\r
9784     if (IsIconic(hwndMain))\r
9785       ShowWindow(hwndMain, SW_RESTORE);\r
9786 \r
9787     SetActiveWindow(hwndMain);\r
9788 }\r
9789 \r
9790 /*\r
9791  * Prototypes for animation support routines\r
9792  */\r
9793 static void ScreenSquare(int column, int row, POINT * pt);\r
9794 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9795      POINT frames[], int * nFrames);\r
9796 \r
9797 \r
9798 #define kFactor 4\r
9799 \r
9800 void\r
9801 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9802 {       // [HGM] atomic: animate blast wave\r
9803         int i;\r
9804 \r
9805         explodeInfo.fromX = fromX;\r
9806         explodeInfo.fromY = fromY;\r
9807         explodeInfo.toX = toX;\r
9808         explodeInfo.toY = toY;\r
9809         for(i=1; i<4*kFactor; i++) {\r
9810             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9811             DrawPosition(FALSE, board);\r
9812             Sleep(appData.animSpeed);\r
9813         }\r
9814         explodeInfo.radius = 0;\r
9815         DrawPosition(TRUE, board);\r
9816 }\r
9817 \r
9818 void\r
9819 AnimateMove(board, fromX, fromY, toX, toY)\r
9820      Board board;\r
9821      int fromX;\r
9822      int fromY;\r
9823      int toX;\r
9824      int toY;\r
9825 {\r
9826   ChessSquare piece;\r
9827   POINT start, finish, mid;\r
9828   POINT frames[kFactor * 2 + 1];\r
9829   int nFrames, n;\r
9830 \r
9831   if (!appData.animate) return;\r
9832   if (doingSizing) return;\r
9833   if (fromY < 0 || fromX < 0) return;\r
9834   piece = board[fromY][fromX];\r
9835   if (piece >= EmptySquare) return;\r
9836 \r
9837   ScreenSquare(fromX, fromY, &start);\r
9838   ScreenSquare(toX, toY, &finish);\r
9839 \r
9840   /* All moves except knight jumps move in straight line */\r
9841   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9842     mid.x = start.x + (finish.x - start.x) / 2;\r
9843     mid.y = start.y + (finish.y - start.y) / 2;\r
9844   } else {\r
9845     /* Knight: make straight movement then diagonal */\r
9846     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9847        mid.x = start.x + (finish.x - start.x) / 2;\r
9848        mid.y = start.y;\r
9849      } else {\r
9850        mid.x = start.x;\r
9851        mid.y = start.y + (finish.y - start.y) / 2;\r
9852      }\r
9853   }\r
9854   \r
9855   /* Don't use as many frames for very short moves */\r
9856   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9857     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9858   else\r
9859     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9860 \r
9861   animInfo.from.x = fromX;\r
9862   animInfo.from.y = fromY;\r
9863   animInfo.to.x = toX;\r
9864   animInfo.to.y = toY;\r
9865   animInfo.lastpos = start;\r
9866   animInfo.piece = piece;\r
9867   for (n = 0; n < nFrames; n++) {\r
9868     animInfo.pos = frames[n];\r
9869     DrawPosition(FALSE, NULL);\r
9870     animInfo.lastpos = animInfo.pos;\r
9871     Sleep(appData.animSpeed);\r
9872   }\r
9873   animInfo.pos = finish;\r
9874   DrawPosition(FALSE, NULL);\r
9875   animInfo.piece = EmptySquare;\r
9876   Explode(board, fromX, fromY, toX, toY);\r
9877 }\r
9878 \r
9879 /*      Convert board position to corner of screen rect and color       */\r
9880 \r
9881 static void\r
9882 ScreenSquare(column, row, pt)\r
9883      int column; int row; POINT * pt;\r
9884 {\r
9885   if (flipView) {\r
9886     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9887     pt->y = lineGap + row * (squareSize + lineGap);\r
9888   } else {\r
9889     pt->x = lineGap + column * (squareSize + lineGap);\r
9890     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9891   }\r
9892 }\r
9893 \r
9894 /*      Generate a series of frame coords from start->mid->finish.\r
9895         The movement rate doubles until the half way point is\r
9896         reached, then halves back down to the final destination,\r
9897         which gives a nice slow in/out effect. The algorithmn\r
9898         may seem to generate too many intermediates for short\r
9899         moves, but remember that the purpose is to attract the\r
9900         viewers attention to the piece about to be moved and\r
9901         then to where it ends up. Too few frames would be less\r
9902         noticeable.                                             */\r
9903 \r
9904 static void\r
9905 Tween(start, mid, finish, factor, frames, nFrames)\r
9906      POINT * start; POINT * mid;\r
9907      POINT * finish; int factor;\r
9908      POINT frames[]; int * nFrames;\r
9909 {\r
9910   int n, fraction = 1, count = 0;\r
9911 \r
9912   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9913   for (n = 0; n < factor; n++)\r
9914     fraction *= 2;\r
9915   for (n = 0; n < factor; n++) {\r
9916     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9917     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9918     count ++;\r
9919     fraction = fraction / 2;\r
9920   }\r
9921   \r
9922   /* Midpoint */\r
9923   frames[count] = *mid;\r
9924   count ++;\r
9925   \r
9926   /* Slow out, stepping 1/2, then 1/4, ... */\r
9927   fraction = 2;\r
9928   for (n = 0; n < factor; n++) {\r
9929     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9930     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9931     count ++;\r
9932     fraction = fraction * 2;\r
9933   }\r
9934   *nFrames = count;\r
9935 }\r
9936 \r
9937 void\r
9938 SettingsPopUp(ChessProgramState *cps)\r
9939 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9940       EngineOptionsPopup(savedHwnd, cps);\r
9941 }\r
9942 \r
9943 int flock(int fid, int code)\r
9944 {\r
9945     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9946     OVERLAPPED ov;\r
9947     ov.hEvent = NULL;\r
9948     ov.Offset = 0;\r
9949     ov.OffsetHigh = 0;\r
9950     switch(code) {\r
9951       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9952       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9953       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9954       default: return -1;\r
9955     }\r
9956     return 0;\r
9957 }\r