Updated copyright notice to 2012
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][41    ] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
257   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
318   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
319   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
322   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
323 { DLG_MoveHistory }, \r
324 { DLG_EvalGraph }, \r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
328   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
329   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
330   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
332   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
333   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
334 { 0 }\r
335 };\r
336 \r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
338 static int lastChecked;\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
340 extern int tinyLayout;\r
341 extern char * menuBarText[][10];\r
342 \r
343 void\r
344 LoadLanguageFile(char *name)\r
345 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
346     FILE *f;\r
347     int i=0, j=0, n=0, k;\r
348     char buf[MSG_SIZ];\r
349 \r
350     if(!name || name[0] == NULLCHAR) return;\r
351       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
352     appData.language = oldLanguage;\r
353     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
354     if((f = fopen(buf, "r")) == NULL) return;\r
355     while((k = fgetc(f)) != EOF) {\r
356         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
357         languageBuf[i] = k;\r
358         if(k == '\n') {\r
359             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
360                 char *p;\r
361                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
362                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
363                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
364                         english[j] = languageBuf + n + 1; *p = 0;\r
365                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
367                     }\r
368                 }\r
369             }\r
370             n = i + 1;\r
371         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
372             switch(k) {\r
373               case 'n': k = '\n'; break;\r
374               case 'r': k = '\r'; break;\r
375               case 't': k = '\t'; break;\r
376             }\r
377             languageBuf[--i] = k;\r
378         }\r
379         i++;\r
380     }\r
381     fclose(f);\r
382     barbaric = (j != 0);\r
383     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
384 }\r
385 \r
386 char *\r
387 T_(char *s)\r
388 {   // return the translation of the given string\r
389     // efficiency can be improved a lot...\r
390     int i=0;\r
391     static char buf[MSG_SIZ];\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
393     if(!barbaric) return s;\r
394     if(!s) return ""; // sanity\r
395     while(english[i]) {\r
396         if(!strcmp(s, english[i])) return foreign[i];\r
397         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
398             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
399             return buf;\r
400         }\r
401         i++;\r
402     }\r
403     return s;\r
404 }\r
405 \r
406 void\r
407 Translate(HWND hDlg, int dialogID)\r
408 {   // translate all text items in the given dialog\r
409     int i=0, j, k;\r
410     char buf[MSG_SIZ], *s;\r
411     if(!barbaric) return;\r
412     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
413     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
414     GetWindowText( hDlg, buf, MSG_SIZ );\r
415     s = T_(buf);\r
416     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
417     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
418         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
419         if(strlen(buf) == 0) continue;\r
420         s = T_(buf);\r
421         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
422     }\r
423 }\r
424 \r
425 HMENU\r
426 TranslateOneMenu(int i, HMENU subMenu)\r
427 {\r
428     int j;\r
429     static MENUITEMINFO info;\r
430 \r
431     info.cbSize = sizeof(MENUITEMINFO);\r
432     info.fMask = MIIM_STATE | MIIM_TYPE;\r
433           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
434             char buf[MSG_SIZ];\r
435             info.dwTypeData = buf;\r
436             info.cch = sizeof(buf);\r
437             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
438             if(i < 10) {
439                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
440                 else menuText[i][j] = strdup(buf); // remember original on first change\r
441             }\r
442             if(buf[0] == NULLCHAR) continue;\r
443             info.dwTypeData = T_(buf);\r
444             info.cch = strlen(buf)+1;\r
445             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
446           }\r
447     return subMenu;\r
448 }\r
449 \r
450 void\r
451 TranslateMenus(int addLanguage)\r
452 {\r
453     int i;\r
454     WIN32_FIND_DATA fileData;\r
455     HANDLE hFind;\r
456 #define IDM_English 1970\r
457     if(1) {\r
458         HMENU mainMenu = GetMenu(hwndMain);\r
459         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
460           HMENU subMenu = GetSubMenu(mainMenu, i);\r
461           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
462                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
463           TranslateOneMenu(i, subMenu);\r
464         }\r
465         DrawMenuBar(hwndMain);\r
466     }\r
467 \r
468     if(!addLanguage) return;\r
469     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
470         HMENU mainMenu = GetMenu(hwndMain);\r
471         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
472         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
473         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
474         i = 0; lastChecked = IDM_English;\r
475         do {\r
476             char *p, *q = fileData.cFileName;\r
477             int checkFlag = MF_UNCHECKED;\r
478             languageFile[i] = strdup(q);\r
479             if(barbaric && !strcmp(oldLanguage, q)) {\r
480                 checkFlag = MF_CHECKED;\r
481                 lastChecked = IDM_English + i + 1;\r
482                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
483             }\r
484             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
485             p = strstr(fileData.cFileName, ".lng");\r
486             if(p) *p = 0;\r
487             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
488         } while(FindNextFile(hFind, &fileData));\r
489         FindClose(hFind);\r
490     }\r
491 }\r
492 \r
493 #endif\r
494 \r
495 typedef struct {\r
496   char *name;\r
497   int squareSize;\r
498   int lineGap;\r
499   int smallLayout;\r
500   int tinyLayout;\r
501   int cliWidth, cliHeight;\r
502 } SizeInfo;\r
503 \r
504 SizeInfo sizeInfo[] = \r
505 {\r
506   { "tiny",     21, 0, 1, 1, 0, 0 },\r
507   { "teeny",    25, 1, 1, 1, 0, 0 },\r
508   { "dinky",    29, 1, 1, 1, 0, 0 },\r
509   { "petite",   33, 1, 1, 1, 0, 0 },\r
510   { "slim",     37, 2, 1, 0, 0, 0 },\r
511   { "small",    40, 2, 1, 0, 0, 0 },\r
512   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
513   { "middling", 49, 2, 0, 0, 0, 0 },\r
514   { "average",  54, 2, 0, 0, 0, 0 },\r
515   { "moderate", 58, 3, 0, 0, 0, 0 },\r
516   { "medium",   64, 3, 0, 0, 0, 0 },\r
517   { "bulky",    72, 3, 0, 0, 0, 0 },\r
518   { "large",    80, 3, 0, 0, 0, 0 },\r
519   { "big",      87, 3, 0, 0, 0, 0 },\r
520   { "huge",     95, 3, 0, 0, 0, 0 },\r
521   { "giant",    108, 3, 0, 0, 0, 0 },\r
522   { "colossal", 116, 4, 0, 0, 0, 0 },\r
523   { "titanic",  129, 4, 0, 0, 0, 0 },\r
524   { NULL, 0, 0, 0, 0, 0, 0 }\r
525 };\r
526 \r
527 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
528 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
529 {\r
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539   { 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
540   { 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
541   { 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
542   { 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
543   { 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
544   { 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
545   { 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
546   { 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
547   { 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
548 };\r
549 \r
550 MyFont *font[NUM_SIZES][NUM_FONTS];\r
551 \r
552 typedef struct {\r
553   char *label;\r
554   int id;\r
555   HWND hwnd;\r
556   WNDPROC wndproc;\r
557 } MyButtonDesc;\r
558 \r
559 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
560 #define N_BUTTONS 5\r
561 \r
562 MyButtonDesc buttonDesc[N_BUTTONS] =\r
563 {\r
564   {"<<", IDM_ToStart, NULL, NULL},\r
565   {"<", IDM_Backward, NULL, NULL},\r
566   {"P", IDM_Pause, NULL, NULL},\r
567   {">", IDM_Forward, NULL, NULL},\r
568   {">>", IDM_ToEnd, NULL, NULL},\r
569 };\r
570 \r
571 int tinyLayout = 0, smallLayout = 0;\r
572 #define MENU_BAR_ITEMS 9\r
573 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
574   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
575   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
576 };\r
577 \r
578 \r
579 MySound sounds[(int)NSoundClasses];\r
580 MyTextAttribs textAttribs[(int)NColorClasses];\r
581 \r
582 MyColorizeAttribs colorizeAttribs[] = {\r
583   { (COLORREF)0, 0, N_("Shout Text") },\r
584   { (COLORREF)0, 0, N_("SShout/CShout") },\r
585   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
586   { (COLORREF)0, 0, N_("Channel Text") },\r
587   { (COLORREF)0, 0, N_("Kibitz Text") },\r
588   { (COLORREF)0, 0, N_("Tell Text") },\r
589   { (COLORREF)0, 0, N_("Challenge Text") },\r
590   { (COLORREF)0, 0, N_("Request Text") },\r
591   { (COLORREF)0, 0, N_("Seek Text") },\r
592   { (COLORREF)0, 0, N_("Normal Text") },\r
593   { (COLORREF)0, 0, N_("None") }\r
594 };\r
595 \r
596 \r
597 \r
598 static char *commentTitle;\r
599 static char *commentText;\r
600 static int commentIndex;\r
601 static Boolean editComment = FALSE;\r
602 \r
603 \r
604 char errorTitle[MSG_SIZ];\r
605 char errorMessage[2*MSG_SIZ];\r
606 HWND errorDialog = NULL;\r
607 BOOLEAN moveErrorMessageUp = FALSE;\r
608 BOOLEAN consoleEcho = TRUE;\r
609 CHARFORMAT consoleCF;\r
610 COLORREF consoleBackgroundColor;\r
611 \r
612 char *programVersion;\r
613 \r
614 #define CPReal 1\r
615 #define CPComm 2\r
616 #define CPSock 3\r
617 #define CPRcmd 4\r
618 typedef int CPKind;\r
619 \r
620 typedef struct {\r
621   CPKind kind;\r
622   HANDLE hProcess;\r
623   DWORD pid;\r
624   HANDLE hTo;\r
625   HANDLE hFrom;\r
626   SOCKET sock;\r
627   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
628 } ChildProc;\r
629 \r
630 #define INPUT_SOURCE_BUF_SIZE 4096\r
631 \r
632 typedef struct _InputSource {\r
633   CPKind kind;\r
634   HANDLE hFile;\r
635   SOCKET sock;\r
636   int lineByLine;\r
637   HANDLE hThread;\r
638   DWORD id;\r
639   char buf[INPUT_SOURCE_BUF_SIZE];\r
640   char *next;\r
641   DWORD count;\r
642   int error;\r
643   InputCallback func;\r
644   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
645   VOIDSTAR closure;\r
646 } InputSource;\r
647 \r
648 InputSource *consoleInputSource;\r
649 \r
650 DCB dcb;\r
651 \r
652 /* forward */\r
653 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
654 VOID ConsoleCreate();\r
655 LRESULT CALLBACK\r
656   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
657 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
658 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
659 VOID ParseCommSettings(char *arg, DCB *dcb);\r
660 LRESULT CALLBACK\r
661   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
662 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
663 void ParseIcsTextMenu(char *icsTextMenuString);\r
664 VOID PopUpNameDialog(char firstchar);\r
665 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
666 \r
667 /* [AS] */\r
668 int NewGameFRC();\r
669 int GameListOptions();\r
670 \r
671 int dummy; // [HGM] for obsolete args\r
672 \r
673 HWND hwndMain = NULL;        /* root window*/\r
674 HWND hwndConsole = NULL;\r
675 HWND commentDialog = NULL;\r
676 HWND moveHistoryDialog = NULL;\r
677 HWND evalGraphDialog = NULL;\r
678 HWND engineOutputDialog = NULL;\r
679 HWND gameListDialog = NULL;\r
680 HWND editTagsDialog = NULL;\r
681 \r
682 int commentUp = FALSE;\r
683 \r
684 WindowPlacement wpMain;\r
685 WindowPlacement wpConsole;\r
686 WindowPlacement wpComment;\r
687 WindowPlacement wpMoveHistory;\r
688 WindowPlacement wpEvalGraph;\r
689 WindowPlacement wpEngineOutput;\r
690 WindowPlacement wpGameList;\r
691 WindowPlacement wpTags;\r
692 \r
693 VOID EngineOptionsPopup(); // [HGM] settings\r
694 \r
695 VOID GothicPopUp(char *title, VariantClass variant);\r
696 /*\r
697  * Setting "frozen" should disable all user input other than deleting\r
698  * the window.  We do this while engines are initializing themselves.\r
699  */\r
700 static int frozen = 0;\r
701 static int oldMenuItemState[MENU_BAR_ITEMS];\r
702 void FreezeUI()\r
703 {\r
704   HMENU hmenu;\r
705   int i;\r
706 \r
707   if (frozen) return;\r
708   frozen = 1;\r
709   hmenu = GetMenu(hwndMain);\r
710   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
711     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
712   }\r
713   DrawMenuBar(hwndMain);\r
714 }\r
715 \r
716 /* Undo a FreezeUI */\r
717 void ThawUI()\r
718 {\r
719   HMENU hmenu;\r
720   int i;\r
721 \r
722   if (!frozen) return;\r
723   frozen = 0;\r
724   hmenu = GetMenu(hwndMain);\r
725   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
726     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
727   }\r
728   DrawMenuBar(hwndMain);\r
729 }\r
730 \r
731 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
732 \r
733 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
734 #ifdef JAWS\r
735 #include "jaws.c"\r
736 #else\r
737 #define JAWS_INIT\r
738 #define JAWS_ARGS\r
739 #define JAWS_ALT_INTERCEPT\r
740 #define JAWS_KBUP_NAVIGATION\r
741 #define JAWS_KBDOWN_NAVIGATION\r
742 #define JAWS_MENU_ITEMS\r
743 #define JAWS_SILENCE\r
744 #define JAWS_REPLAY\r
745 #define JAWS_ACCEL\r
746 #define JAWS_COPYRIGHT\r
747 #define JAWS_DELETE(X) X\r
748 #define SAYMACHINEMOVE()\r
749 #define SAY(X)\r
750 #endif\r
751 \r
752 /*---------------------------------------------------------------------------*\\r
753  *\r
754  * WinMain\r
755  *\r
756 \*---------------------------------------------------------------------------*/\r
757 \r
758 int APIENTRY\r
759 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
760         LPSTR lpCmdLine, int nCmdShow)\r
761 {\r
762   MSG msg;\r
763   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
764 //  INITCOMMONCONTROLSEX ex;\r
765 \r
766   debugFP = stderr;\r
767 \r
768   LoadLibrary("RICHED32.DLL");\r
769   consoleCF.cbSize = sizeof(CHARFORMAT);\r
770 \r
771   if (!InitApplication(hInstance)) {\r
772     return (FALSE);\r
773   }\r
774   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
775     return (FALSE);\r
776   }\r
777 \r
778   JAWS_INIT\r
779   TranslateMenus(1);\r
780 \r
781 //  InitCommonControlsEx(&ex);\r
782   InitCommonControls();\r
783 \r
784   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
785   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
786   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
787 \r
788   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
789 \r
790   while (GetMessage(&msg, /* message structure */\r
791                     NULL, /* handle of window receiving the message */\r
792                     0,    /* lowest message to examine */\r
793                     0))   /* highest message to examine */\r
794     {\r
795 \r
796       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
797         // [HGM] navigate: switch between all windows with tab\r
798         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
799         int i, currentElement = 0;\r
800 \r
801         // first determine what element of the chain we come from (if any)\r
802         if(appData.icsActive) {\r
803             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
804             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
805         }\r
806         if(engineOutputDialog && EngineOutputIsUp()) {\r
807             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
808             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
809         }\r
810         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
811             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
812         }\r
813         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
814         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
815         if(msg.hwnd == e1)                 currentElement = 2; else\r
816         if(msg.hwnd == e2)                 currentElement = 3; else\r
817         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
818         if(msg.hwnd == mh)                currentElement = 4; else\r
819         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
820         if(msg.hwnd == hText)  currentElement = 5; else\r
821         if(msg.hwnd == hInput) currentElement = 6; else\r
822         for (i = 0; i < N_BUTTONS; i++) {\r
823             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
824         }\r
825 \r
826         // determine where to go to\r
827         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
828           do {\r
829             currentElement = (currentElement + direction) % 7;\r
830             switch(currentElement) {\r
831                 case 0:\r
832                   h = hwndMain; break; // passing this case always makes the loop exit\r
833                 case 1:\r
834                   h = buttonDesc[0].hwnd; break; // could be NULL\r
835                 case 2:\r
836                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
837                   h = e1; break;\r
838                 case 3:\r
839                   if(!EngineOutputIsUp()) continue;\r
840                   h = e2; break;\r
841                 case 4:\r
842                   if(!MoveHistoryIsUp()) continue;\r
843                   h = mh; break;\r
844 //              case 6: // input to eval graph does not seem to get here!\r
845 //                if(!EvalGraphIsUp()) continue;\r
846 //                h = evalGraphDialog; break;\r
847                 case 5:\r
848                   if(!appData.icsActive) continue;\r
849                   SAY("display");\r
850                   h = hText; break;\r
851                 case 6:\r
852                   if(!appData.icsActive) continue;\r
853                   SAY("input");\r
854                   h = hInput; break;\r
855             }\r
856           } while(h == 0);\r
857 \r
858           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
859           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
860           SetFocus(h);\r
861 \r
862           continue; // this message now has been processed\r
863         }\r
864       }\r
865 \r
866       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
867           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
868           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
869           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
870           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
871           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
872           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
873           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
874           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
875           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
876         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
877         for(i=0; i<MAX_CHAT; i++) \r
878             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
879                 done = 1; break;\r
880         }\r
881         if(done) continue; // [HGM] chat: end patch\r
882         TranslateMessage(&msg); /* Translates virtual key codes */\r
883         DispatchMessage(&msg);  /* Dispatches message to window */\r
884       }\r
885     }\r
886 \r
887 \r
888   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
889 }\r
890 \r
891 /*---------------------------------------------------------------------------*\\r
892  *\r
893  * Initialization functions\r
894  *\r
895 \*---------------------------------------------------------------------------*/\r
896 \r
897 void\r
898 SetUserLogo()\r
899 {   // update user logo if necessary\r
900     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
901 \r
902     if(appData.autoLogo) {\r
903           curName = UserName();\r
904           if(strcmp(curName, oldUserName)) {\r
905                 GetCurrentDirectory(MSG_SIZ, dir);\r
906                 SetCurrentDirectory(installDir);\r
907                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
908                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
909                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
910                 if(userLogo == NULL)\r
911                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
912                 SetCurrentDirectory(dir); /* return to prev directory */\r
913           }\r
914     }\r
915 }\r
916 \r
917 BOOL\r
918 InitApplication(HINSTANCE hInstance)\r
919 {\r
920   WNDCLASS wc;\r
921 \r
922   /* Fill in window class structure with parameters that describe the */\r
923   /* main window. */\r
924 \r
925   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
926   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
927   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
928   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
929   wc.hInstance     = hInstance;         /* Owner of this class */\r
930   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
931   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
932   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
933   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
934   wc.lpszClassName = szAppName;                 /* Name to register as */\r
935 \r
936   /* Register the window class and return success/failure code. */\r
937   if (!RegisterClass(&wc)) return FALSE;\r
938 \r
939   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
940   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
941   wc.cbClsExtra    = 0;\r
942   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
943   wc.hInstance     = hInstance;\r
944   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
945   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
946   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
947   wc.lpszMenuName  = NULL;\r
948   wc.lpszClassName = szConsoleName;\r
949 \r
950   if (!RegisterClass(&wc)) return FALSE;\r
951   return TRUE;\r
952 }\r
953 \r
954 \r
955 /* Set by InitInstance, used by EnsureOnScreen */\r
956 int screenHeight, screenWidth;\r
957 \r
958 void\r
959 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
960 {\r
961 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
962   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
963   if (*x > screenWidth - 32) *x = 0;\r
964   if (*y > screenHeight - 32) *y = 0;\r
965   if (*x < minX) *x = minX;\r
966   if (*y < minY) *y = minY;\r
967 }\r
968 \r
969 VOID\r
970 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
971 {\r
972   char buf[MSG_SIZ], dir[MSG_SIZ];\r
973   GetCurrentDirectory(MSG_SIZ, dir);\r
974   SetCurrentDirectory(installDir);\r
975   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
976       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
977 \r
978       if (cps->programLogo == NULL && appData.debugMode) {\r
979           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
980       }\r
981   } else if(appData.autoLogo) {\r
982       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
983         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
984         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
985       } else\r
986       if(appData.directory[n] && appData.directory[n][0]) {\r
987         SetCurrentDirectory(appData.directory[n]);\r
988         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
989       }\r
990   }\r
991   SetCurrentDirectory(dir); /* return to prev directory */\r
992 }\r
993 \r
994 VOID\r
995 InitTextures()\r
996 {\r
997   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
998   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
999   \r
1000   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1001       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1002       liteBackTextureMode = appData.liteBackTextureMode;\r
1003 \r
1004       if (liteBackTexture == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1006       }\r
1007   }\r
1008   \r
1009   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1010       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1011       darkBackTextureMode = appData.darkBackTextureMode;\r
1012 \r
1013       if (darkBackTexture == NULL && appData.debugMode) {\r
1014           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1015       }\r
1016   }\r
1017 }\r
1018 \r
1019 BOOL\r
1020 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1021 {\r
1022   HWND hwnd; /* Main window handle. */\r
1023   int ibs;\r
1024   WINDOWPLACEMENT wp;\r
1025   char *filepart;\r
1026 \r
1027   hInst = hInstance;    /* Store instance handle in our global variable */\r
1028   programName = szAppName;\r
1029 \r
1030   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1031     *filepart = NULLCHAR;\r
1032   } else {\r
1033     GetCurrentDirectory(MSG_SIZ, installDir);\r
1034   }\r
1035   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1036   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1037   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1038   /* xboard, and older WinBoards, controlled the move sound with the\r
1039      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1040      always turn the option on (so that the backend will call us),\r
1041      then let the user turn the sound off by setting it to silence if\r
1042      desired.  To accommodate old winboard.ini files saved by old\r
1043      versions of WinBoard, we also turn off the sound if the option\r
1044      was initially set to false. [HGM] taken out of InitAppData */\r
1045   if (!appData.ringBellAfterMoves) {\r
1046     sounds[(int)SoundMove].name = strdup("");\r
1047     appData.ringBellAfterMoves = TRUE;\r
1048   }\r
1049   if (appData.debugMode) {\r
1050     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1051     setbuf(debugFP, NULL);\r
1052   }\r
1053 \r
1054   LoadLanguageFile(appData.language);\r
1055 \r
1056   InitBackEnd1();\r
1057 \r
1058 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1059 //  InitEngineUCI( installDir, &second );\r
1060 \r
1061   /* Create a main window for this application instance. */\r
1062   hwnd = CreateWindow(szAppName, szTitle,\r
1063                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1064                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1065                       NULL, NULL, hInstance, NULL);\r
1066   hwndMain = hwnd;\r
1067 \r
1068   /* If window could not be created, return "failure" */\r
1069   if (!hwnd) {\r
1070     return (FALSE);\r
1071   }\r
1072 \r
1073   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1074   LoadLogo(&first, 0, FALSE);\r
1075   LoadLogo(&second, 1, appData.icsActive);\r
1076 \r
1077   SetUserLogo();\r
1078 \r
1079   iconWhite = LoadIcon(hInstance, "icon_white");\r
1080   iconBlack = LoadIcon(hInstance, "icon_black");\r
1081   iconCurrent = iconWhite;\r
1082   InitDrawingColors();\r
1083   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1084   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1085   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1086     /* Compute window size for each board size, and use the largest\r
1087        size that fits on this screen as the default. */\r
1088     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1089     if (boardSize == (BoardSize)-1 &&\r
1090         winH <= screenHeight\r
1091            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1092         && winW <= screenWidth) {\r
1093       boardSize = (BoardSize)ibs;\r
1094     }\r
1095   }\r
1096 \r
1097   InitDrawingSizes(boardSize, 0);\r
1098   InitMenuChecks();\r
1099   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1100 \r
1101   /* [AS] Load textures if specified */\r
1102   InitTextures();\r
1103 \r
1104   mysrandom( (unsigned) time(NULL) );\r
1105 \r
1106   /* [AS] Restore layout */\r
1107   if( wpMoveHistory.visible ) {\r
1108       MoveHistoryPopUp();\r
1109   }\r
1110 \r
1111   if( wpEvalGraph.visible ) {\r
1112       EvalGraphPopUp();\r
1113   }\r
1114 \r
1115   if( wpEngineOutput.visible ) {\r
1116       EngineOutputPopUp();\r
1117   }\r
1118 \r
1119   /* Make the window visible; update its client area; and return "success" */\r
1120   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1121   wp.length = sizeof(WINDOWPLACEMENT);\r
1122   wp.flags = 0;\r
1123   wp.showCmd = nCmdShow;\r
1124   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1125   wp.rcNormalPosition.left = wpMain.x;\r
1126   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1127   wp.rcNormalPosition.top = wpMain.y;\r
1128   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1129   SetWindowPlacement(hwndMain, &wp);\r
1130 \r
1131   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1132 \r
1133   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1134                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1135 \r
1136   if (hwndConsole) {\r
1137 #if AOT_CONSOLE\r
1138     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1139                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1140 #endif\r
1141     ShowWindow(hwndConsole, nCmdShow);\r
1142     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1143       char buf[MSG_SIZ], *p = buf, *q;\r
1144         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1145       do {\r
1146         q = strchr(p, ';');\r
1147         if(q) *q++ = 0;\r
1148         if(*p) ChatPopUp(p);\r
1149       } while(p=q);\r
1150     }\r
1151     SetActiveWindow(hwndConsole);\r
1152   }\r
1153   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1154   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1155 \r
1156   return TRUE;\r
1157 \r
1158 }\r
1159 \r
1160 VOID\r
1161 InitMenuChecks()\r
1162 {\r
1163   HMENU hmenu = GetMenu(hwndMain);\r
1164 \r
1165   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1166                         MF_BYCOMMAND|((appData.icsActive &&\r
1167                                        *appData.icsCommPort != NULLCHAR) ?\r
1168                                       MF_ENABLED : MF_GRAYED));\r
1169   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1170                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1171                                      MF_CHECKED : MF_UNCHECKED));\r
1172 }\r
1173 \r
1174 //---------------------------------------------------------------------------------------------------------\r
1175 \r
1176 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1177 #define XBOARD FALSE\r
1178 \r
1179 #define OPTCHAR "/"\r
1180 #define SEPCHAR "="\r
1181 \r
1182 #include "args.h"\r
1183 \r
1184 // front-end part of option handling\r
1185 \r
1186 VOID\r
1187 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1188 {\r
1189   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1190   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1191   DeleteDC(hdc);\r
1192   lf->lfWidth = 0;\r
1193   lf->lfEscapement = 0;\r
1194   lf->lfOrientation = 0;\r
1195   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1196   lf->lfItalic = mfp->italic;\r
1197   lf->lfUnderline = mfp->underline;\r
1198   lf->lfStrikeOut = mfp->strikeout;\r
1199   lf->lfCharSet = mfp->charset;\r
1200   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1201   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1202   lf->lfQuality = DEFAULT_QUALITY;\r
1203   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1204     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1205 }\r
1206 \r
1207 void\r
1208 CreateFontInMF(MyFont *mf)\r
1209\r
1210   LFfromMFP(&mf->lf, &mf->mfp);\r
1211   if (mf->hf) DeleteObject(mf->hf);\r
1212   mf->hf = CreateFontIndirect(&mf->lf);\r
1213 }\r
1214 \r
1215 // [HGM] This platform-dependent table provides the location for storing the color info\r
1216 void *\r
1217 colorVariable[] = {\r
1218   &whitePieceColor, \r
1219   &blackPieceColor, \r
1220   &lightSquareColor,\r
1221   &darkSquareColor, \r
1222   &highlightSquareColor,\r
1223   &premoveHighlightColor,\r
1224   NULL,\r
1225   &consoleBackgroundColor,\r
1226   &appData.fontForeColorWhite,\r
1227   &appData.fontBackColorWhite,\r
1228   &appData.fontForeColorBlack,\r
1229   &appData.fontBackColorBlack,\r
1230   &appData.evalHistColorWhite,\r
1231   &appData.evalHistColorBlack,\r
1232   &appData.highlightArrowColor,\r
1233 };\r
1234 \r
1235 /* Command line font name parser.  NULL name means do nothing.\r
1236    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1237    For backward compatibility, syntax without the colon is also\r
1238    accepted, but font names with digits in them won't work in that case.\r
1239 */\r
1240 VOID\r
1241 ParseFontName(char *name, MyFontParams *mfp)\r
1242 {\r
1243   char *p, *q;\r
1244   if (name == NULL) return;\r
1245   p = name;\r
1246   q = strchr(p, ':');\r
1247   if (q) {\r
1248     if (q - p >= sizeof(mfp->faceName))\r
1249       ExitArgError(_("Font name too long:"), name, TRUE);\r
1250     memcpy(mfp->faceName, p, q - p);\r
1251     mfp->faceName[q - p] = NULLCHAR;\r
1252     p = q + 1;\r
1253   } else {\r
1254     q = mfp->faceName;\r
1255     while (*p && !isdigit(*p)) {\r
1256       *q++ = *p++;\r
1257       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1258         ExitArgError(_("Font name too long:"), name, TRUE);\r
1259     }\r
1260     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1261     *q = NULLCHAR;\r
1262   }\r
1263   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1264   mfp->pointSize = (float) atof(p);\r
1265   mfp->bold = (strchr(p, 'b') != NULL);\r
1266   mfp->italic = (strchr(p, 'i') != NULL);\r
1267   mfp->underline = (strchr(p, 'u') != NULL);\r
1268   mfp->strikeout = (strchr(p, 's') != NULL);\r
1269   mfp->charset = DEFAULT_CHARSET;\r
1270   q = strchr(p, 'c');\r
1271   if (q)\r
1272     mfp->charset = (BYTE) atoi(q+1);\r
1273 }\r
1274 \r
1275 void\r
1276 ParseFont(char *name, int number)\r
1277 { // wrapper to shield back-end from 'font'\r
1278   ParseFontName(name, &font[boardSize][number]->mfp);\r
1279 }\r
1280 \r
1281 void\r
1282 SetFontDefaults()\r
1283 { // in WB  we have a 2D array of fonts; this initializes their description\r
1284   int i, j;\r
1285   /* Point font array elements to structures and\r
1286      parse default font names */\r
1287   for (i=0; i<NUM_FONTS; i++) {\r
1288     for (j=0; j<NUM_SIZES; j++) {\r
1289       font[j][i] = &fontRec[j][i];\r
1290       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1291     }\r
1292   }\r
1293 }\r
1294 \r
1295 void\r
1296 CreateFonts()\r
1297 { // here we create the actual fonts from the selected descriptions\r
1298   int i, j;\r
1299   for (i=0; i<NUM_FONTS; i++) {\r
1300     for (j=0; j<NUM_SIZES; j++) {\r
1301       CreateFontInMF(font[j][i]);\r
1302     }\r
1303   }\r
1304 }\r
1305 /* Color name parser.\r
1306    X version accepts X color names, but this one\r
1307    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1308 COLORREF\r
1309 ParseColorName(char *name)\r
1310 {\r
1311   int red, green, blue, count;\r
1312   char buf[MSG_SIZ];\r
1313 \r
1314   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1315   if (count != 3) {\r
1316     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1317       &red, &green, &blue);\r
1318   }\r
1319   if (count != 3) {\r
1320     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1321     DisplayError(buf, 0);\r
1322     return RGB(0, 0, 0);\r
1323   }\r
1324   return PALETTERGB(red, green, blue);\r
1325 }\r
1326 \r
1327 void\r
1328 ParseColor(int n, char *name)\r
1329 { // for WinBoard the color is an int, which needs to be derived from the string\r
1330   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1331 }\r
1332 \r
1333 void\r
1334 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1335 {\r
1336   char *e = argValue;\r
1337   int eff = 0;\r
1338 \r
1339   while (*e) {\r
1340     if (*e == 'b')      eff |= CFE_BOLD;\r
1341     else if (*e == 'i') eff |= CFE_ITALIC;\r
1342     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1343     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1344     else if (*e == '#' || isdigit(*e)) break;\r
1345     e++;\r
1346   }\r
1347   *effects = eff;\r
1348   *color   = ParseColorName(e);\r
1349 }\r
1350 \r
1351 void\r
1352 ParseTextAttribs(ColorClass cc, char *s)\r
1353 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1354     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1355     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1356 }\r
1357 \r
1358 void\r
1359 ParseBoardSize(void *addr, char *name)\r
1360 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1361   BoardSize bs = SizeTiny;\r
1362   while (sizeInfo[bs].name != NULL) {\r
1363     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1364         *(BoardSize *)addr = bs;\r
1365         return;\r
1366     }\r
1367     bs++;\r
1368   }\r
1369   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1370 }\r
1371 \r
1372 void\r
1373 LoadAllSounds()\r
1374 { // [HGM] import name from appData first\r
1375   ColorClass cc;\r
1376   SoundClass sc;\r
1377   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1378     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1379     textAttribs[cc].sound.data = NULL;\r
1380     MyLoadSound(&textAttribs[cc].sound);\r
1381   }\r
1382   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1383     textAttribs[cc].sound.name = strdup("");\r
1384     textAttribs[cc].sound.data = NULL;\r
1385   }\r
1386   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1387     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1388     sounds[sc].data = NULL;\r
1389     MyLoadSound(&sounds[sc]);\r
1390   }\r
1391 }\r
1392 \r
1393 void\r
1394 SetCommPortDefaults()\r
1395 {\r
1396    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1397   dcb.DCBlength = sizeof(DCB);\r
1398   dcb.BaudRate = 9600;\r
1399   dcb.fBinary = TRUE;\r
1400   dcb.fParity = FALSE;\r
1401   dcb.fOutxCtsFlow = FALSE;\r
1402   dcb.fOutxDsrFlow = FALSE;\r
1403   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1404   dcb.fDsrSensitivity = FALSE;\r
1405   dcb.fTXContinueOnXoff = TRUE;\r
1406   dcb.fOutX = FALSE;\r
1407   dcb.fInX = FALSE;\r
1408   dcb.fNull = FALSE;\r
1409   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1410   dcb.fAbortOnError = FALSE;\r
1411   dcb.ByteSize = 7;\r
1412   dcb.Parity = SPACEPARITY;\r
1413   dcb.StopBits = ONESTOPBIT;\r
1414 }\r
1415 \r
1416 // [HGM] args: these three cases taken out to stay in front-end\r
1417 void\r
1418 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1419 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1420         // while the curent board size determines the element. This system should be ported to XBoard.\r
1421         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1422         int bs;\r
1423         for (bs=0; bs<NUM_SIZES; bs++) {\r
1424           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1425           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1426           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1427             ad->argName, mfp->faceName, mfp->pointSize,\r
1428             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1429             mfp->bold ? "b" : "",\r
1430             mfp->italic ? "i" : "",\r
1431             mfp->underline ? "u" : "",\r
1432             mfp->strikeout ? "s" : "",\r
1433             (int)mfp->charset);\r
1434         }\r
1435       }\r
1436 \r
1437 void\r
1438 ExportSounds()\r
1439 { // [HGM] copy the names from the internal WB variables to appData\r
1440   ColorClass cc;\r
1441   SoundClass sc;\r
1442   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1443     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1444   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1445     (&appData.soundMove)[sc] = sounds[sc].name;\r
1446 }\r
1447 \r
1448 void\r
1449 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1450 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1451         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1452         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1453           (ta->effects & CFE_BOLD) ? "b" : "",\r
1454           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1455           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1456           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1457           (ta->effects) ? " " : "",\r
1458           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1459       }\r
1460 \r
1461 void\r
1462 SaveColor(FILE *f, ArgDescriptor *ad)\r
1463 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1464         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1465         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1466           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1467 }\r
1468 \r
1469 void\r
1470 SaveBoardSize(FILE *f, char *name, void *addr)\r
1471 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1472   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1473 }\r
1474 \r
1475 void\r
1476 ParseCommPortSettings(char *s)\r
1477 { // wrapper to keep dcb from back-end\r
1478   ParseCommSettings(s, &dcb);\r
1479 }\r
1480 \r
1481 void\r
1482 GetWindowCoords()\r
1483 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1484   GetActualPlacement(hwndMain, &wpMain);\r
1485   GetActualPlacement(hwndConsole, &wpConsole);\r
1486   GetActualPlacement(commentDialog, &wpComment);\r
1487   GetActualPlacement(editTagsDialog, &wpTags);\r
1488   GetActualPlacement(gameListDialog, &wpGameList);\r
1489   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1490   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1491   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1492 }\r
1493 \r
1494 void\r
1495 PrintCommPortSettings(FILE *f, char *name)\r
1496 { // wrapper to shield back-end from DCB\r
1497       PrintCommSettings(f, name, &dcb);\r
1498 }\r
1499 \r
1500 int\r
1501 MySearchPath(char *installDir, char *name, char *fullname)\r
1502 {\r
1503   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1504   if(name[0]== '%') {\r
1505     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1506     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1507       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1508       *strchr(buf, '%') = 0;\r
1509       strcat(fullname, getenv(buf));\r
1510       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1511     }\r
1512     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1513     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1514     return (int) strlen(fullname);\r
1515   }\r
1516   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1517 }\r
1518 \r
1519 int\r
1520 MyGetFullPathName(char *name, char *fullname)\r
1521 {\r
1522   char *dummy;\r
1523   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1524 }\r
1525 \r
1526 int\r
1527 MainWindowUp()\r
1528 { // [HGM] args: allows testing if main window is realized from back-end\r
1529   return hwndMain != NULL;\r
1530 }\r
1531 \r
1532 void\r
1533 PopUpStartupDialog()\r
1534 {\r
1535     FARPROC lpProc;\r
1536     \r
1537     LoadLanguageFile(appData.language);\r
1538     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1539     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1540     FreeProcInstance(lpProc);\r
1541 }\r
1542 \r
1543 /*---------------------------------------------------------------------------*\\r
1544  *\r
1545  * GDI board drawing routines\r
1546  *\r
1547 \*---------------------------------------------------------------------------*/\r
1548 \r
1549 /* [AS] Draw square using background texture */\r
1550 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1551 {\r
1552     XFORM   x;\r
1553 \r
1554     if( mode == 0 ) {\r
1555         return; /* Should never happen! */\r
1556     }\r
1557 \r
1558     SetGraphicsMode( dst, GM_ADVANCED );\r
1559 \r
1560     switch( mode ) {\r
1561     case 1:\r
1562         /* Identity */\r
1563         break;\r
1564     case 2:\r
1565         /* X reflection */\r
1566         x.eM11 = -1.0;\r
1567         x.eM12 = 0;\r
1568         x.eM21 = 0;\r
1569         x.eM22 = 1.0;\r
1570         x.eDx = (FLOAT) dw + dx - 1;\r
1571         x.eDy = 0;\r
1572         dx = 0;\r
1573         SetWorldTransform( dst, &x );\r
1574         break;\r
1575     case 3:\r
1576         /* Y reflection */\r
1577         x.eM11 = 1.0;\r
1578         x.eM12 = 0;\r
1579         x.eM21 = 0;\r
1580         x.eM22 = -1.0;\r
1581         x.eDx = 0;\r
1582         x.eDy = (FLOAT) dh + dy - 1;\r
1583         dy = 0;\r
1584         SetWorldTransform( dst, &x );\r
1585         break;\r
1586     case 4:\r
1587         /* X/Y flip */\r
1588         x.eM11 = 0;\r
1589         x.eM12 = 1.0;\r
1590         x.eM21 = 1.0;\r
1591         x.eM22 = 0;\r
1592         x.eDx = (FLOAT) dx;\r
1593         x.eDy = (FLOAT) dy;\r
1594         dx = 0;\r
1595         dy = 0;\r
1596         SetWorldTransform( dst, &x );\r
1597         break;\r
1598     }\r
1599 \r
1600     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1601 \r
1602     x.eM11 = 1.0;\r
1603     x.eM12 = 0;\r
1604     x.eM21 = 0;\r
1605     x.eM22 = 1.0;\r
1606     x.eDx = 0;\r
1607     x.eDy = 0;\r
1608     SetWorldTransform( dst, &x );\r
1609 \r
1610     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1611 }\r
1612 \r
1613 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1614 enum {\r
1615     PM_WP = (int) WhitePawn, \r
1616     PM_WN = (int) WhiteKnight, \r
1617     PM_WB = (int) WhiteBishop, \r
1618     PM_WR = (int) WhiteRook, \r
1619     PM_WQ = (int) WhiteQueen, \r
1620     PM_WF = (int) WhiteFerz, \r
1621     PM_WW = (int) WhiteWazir, \r
1622     PM_WE = (int) WhiteAlfil, \r
1623     PM_WM = (int) WhiteMan, \r
1624     PM_WO = (int) WhiteCannon, \r
1625     PM_WU = (int) WhiteUnicorn, \r
1626     PM_WH = (int) WhiteNightrider, \r
1627     PM_WA = (int) WhiteAngel, \r
1628     PM_WC = (int) WhiteMarshall, \r
1629     PM_WAB = (int) WhiteCardinal, \r
1630     PM_WD = (int) WhiteDragon, \r
1631     PM_WL = (int) WhiteLance, \r
1632     PM_WS = (int) WhiteCobra, \r
1633     PM_WV = (int) WhiteFalcon, \r
1634     PM_WSG = (int) WhiteSilver, \r
1635     PM_WG = (int) WhiteGrasshopper, \r
1636     PM_WK = (int) WhiteKing,\r
1637     PM_BP = (int) BlackPawn, \r
1638     PM_BN = (int) BlackKnight, \r
1639     PM_BB = (int) BlackBishop, \r
1640     PM_BR = (int) BlackRook, \r
1641     PM_BQ = (int) BlackQueen, \r
1642     PM_BF = (int) BlackFerz, \r
1643     PM_BW = (int) BlackWazir, \r
1644     PM_BE = (int) BlackAlfil, \r
1645     PM_BM = (int) BlackMan,\r
1646     PM_BO = (int) BlackCannon, \r
1647     PM_BU = (int) BlackUnicorn, \r
1648     PM_BH = (int) BlackNightrider, \r
1649     PM_BA = (int) BlackAngel, \r
1650     PM_BC = (int) BlackMarshall, \r
1651     PM_BG = (int) BlackGrasshopper, \r
1652     PM_BAB = (int) BlackCardinal,\r
1653     PM_BD = (int) BlackDragon,\r
1654     PM_BL = (int) BlackLance,\r
1655     PM_BS = (int) BlackCobra,\r
1656     PM_BV = (int) BlackFalcon,\r
1657     PM_BSG = (int) BlackSilver,\r
1658     PM_BK = (int) BlackKing\r
1659 };\r
1660 \r
1661 static HFONT hPieceFont = NULL;\r
1662 static HBITMAP hPieceMask[(int) EmptySquare];\r
1663 static HBITMAP hPieceFace[(int) EmptySquare];\r
1664 static int fontBitmapSquareSize = 0;\r
1665 static char pieceToFontChar[(int) EmptySquare] =\r
1666                               { 'p', 'n', 'b', 'r', 'q', \r
1667                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1668                       'k', 'o', 'm', 'v', 't', 'w', \r
1669                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1670                                                               'l' };\r
1671 \r
1672 extern BOOL SetCharTable( char *table, const char * map );\r
1673 /* [HGM] moved to backend.c */\r
1674 \r
1675 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1676 {\r
1677     HBRUSH hbrush;\r
1678     BYTE r1 = GetRValue( color );\r
1679     BYTE g1 = GetGValue( color );\r
1680     BYTE b1 = GetBValue( color );\r
1681     BYTE r2 = r1 / 2;\r
1682     BYTE g2 = g1 / 2;\r
1683     BYTE b2 = b1 / 2;\r
1684     RECT rc;\r
1685 \r
1686     /* Create a uniform background first */\r
1687     hbrush = CreateSolidBrush( color );\r
1688     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1689     FillRect( hdc, &rc, hbrush );\r
1690     DeleteObject( hbrush );\r
1691     \r
1692     if( mode == 1 ) {\r
1693         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1694         int steps = squareSize / 2;\r
1695         int i;\r
1696 \r
1697         for( i=0; i<steps; i++ ) {\r
1698             BYTE r = r1 - (r1-r2) * i / steps;\r
1699             BYTE g = g1 - (g1-g2) * i / steps;\r
1700             BYTE b = b1 - (b1-b2) * i / steps;\r
1701 \r
1702             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1703             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1704             FillRect( hdc, &rc, hbrush );\r
1705             DeleteObject(hbrush);\r
1706         }\r
1707     }\r
1708     else if( mode == 2 ) {\r
1709         /* Diagonal gradient, good more or less for every piece */\r
1710         POINT triangle[3];\r
1711         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1712         HBRUSH hbrush_old;\r
1713         int steps = squareSize;\r
1714         int i;\r
1715 \r
1716         triangle[0].x = squareSize - steps;\r
1717         triangle[0].y = squareSize;\r
1718         triangle[1].x = squareSize;\r
1719         triangle[1].y = squareSize;\r
1720         triangle[2].x = squareSize;\r
1721         triangle[2].y = squareSize - steps;\r
1722 \r
1723         for( i=0; i<steps; i++ ) {\r
1724             BYTE r = r1 - (r1-r2) * i / steps;\r
1725             BYTE g = g1 - (g1-g2) * i / steps;\r
1726             BYTE b = b1 - (b1-b2) * i / steps;\r
1727 \r
1728             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1729             hbrush_old = SelectObject( hdc, hbrush );\r
1730             Polygon( hdc, triangle, 3 );\r
1731             SelectObject( hdc, hbrush_old );\r
1732             DeleteObject(hbrush);\r
1733             triangle[0].x++;\r
1734             triangle[2].y++;\r
1735         }\r
1736 \r
1737         SelectObject( hdc, hpen );\r
1738     }\r
1739 }\r
1740 \r
1741 /*\r
1742     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1743     seems to work ok. The main problem here is to find the "inside" of a chess\r
1744     piece: follow the steps as explained below.\r
1745 */\r
1746 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1747 {\r
1748     HBITMAP hbm;\r
1749     HBITMAP hbm_old;\r
1750     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1751     RECT rc;\r
1752     SIZE sz;\r
1753     POINT pt;\r
1754     int backColor = whitePieceColor; \r
1755     int foreColor = blackPieceColor;\r
1756     \r
1757     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1758         backColor = appData.fontBackColorWhite;\r
1759         foreColor = appData.fontForeColorWhite;\r
1760     }\r
1761     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1762         backColor = appData.fontBackColorBlack;\r
1763         foreColor = appData.fontForeColorBlack;\r
1764     }\r
1765 \r
1766     /* Mask */\r
1767     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1768 \r
1769     hbm_old = SelectObject( hdc, hbm );\r
1770 \r
1771     rc.left = 0;\r
1772     rc.top = 0;\r
1773     rc.right = squareSize;\r
1774     rc.bottom = squareSize;\r
1775 \r
1776     /* Step 1: background is now black */\r
1777     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1778 \r
1779     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1780 \r
1781     pt.x = (squareSize - sz.cx) / 2;\r
1782     pt.y = (squareSize - sz.cy) / 2;\r
1783 \r
1784     SetBkMode( hdc, TRANSPARENT );\r
1785     SetTextColor( hdc, chroma );\r
1786     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1787     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1788 \r
1789     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1790     /* Step 3: the area outside the piece is filled with white */\r
1791 //    FloodFill( hdc, 0, 0, chroma );\r
1792     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1793     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1794     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1795     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1796     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1797     /* \r
1798         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1799         but if the start point is not inside the piece we're lost!\r
1800         There should be a better way to do this... if we could create a region or path\r
1801         from the fill operation we would be fine for example.\r
1802     */\r
1803 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1804     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1805 \r
1806     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1807         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1808         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1809 \r
1810         SelectObject( dc2, bm2 );\r
1811         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1812         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1813         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1814         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1815         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1816 \r
1817         DeleteDC( dc2 );\r
1818         DeleteObject( bm2 );\r
1819     }\r
1820 \r
1821     SetTextColor( hdc, 0 );\r
1822     /* \r
1823         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1824         draw the piece again in black for safety.\r
1825     */\r
1826     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1827 \r
1828     SelectObject( hdc, hbm_old );\r
1829 \r
1830     if( hPieceMask[index] != NULL ) {\r
1831         DeleteObject( hPieceMask[index] );\r
1832     }\r
1833 \r
1834     hPieceMask[index] = hbm;\r
1835 \r
1836     /* Face */\r
1837     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1838 \r
1839     SelectObject( hdc, hbm );\r
1840 \r
1841     {\r
1842         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1843         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1844         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1845 \r
1846         SelectObject( dc1, hPieceMask[index] );\r
1847         SelectObject( dc2, bm2 );\r
1848         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1849         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1850         \r
1851         /* \r
1852             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1853             the piece background and deletes (makes transparent) the rest.\r
1854             Thanks to that mask, we are free to paint the background with the greates\r
1855             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1856             We use this, to make gradients and give the pieces a "roundish" look.\r
1857         */\r
1858         SetPieceBackground( hdc, backColor, 2 );\r
1859         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1860 \r
1861         DeleteDC( dc2 );\r
1862         DeleteDC( dc1 );\r
1863         DeleteObject( bm2 );\r
1864     }\r
1865 \r
1866     SetTextColor( hdc, foreColor );\r
1867     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1868 \r
1869     SelectObject( hdc, hbm_old );\r
1870 \r
1871     if( hPieceFace[index] != NULL ) {\r
1872         DeleteObject( hPieceFace[index] );\r
1873     }\r
1874 \r
1875     hPieceFace[index] = hbm;\r
1876 }\r
1877 \r
1878 static int TranslatePieceToFontPiece( int piece )\r
1879 {\r
1880     switch( piece ) {\r
1881     case BlackPawn:\r
1882         return PM_BP;\r
1883     case BlackKnight:\r
1884         return PM_BN;\r
1885     case BlackBishop:\r
1886         return PM_BB;\r
1887     case BlackRook:\r
1888         return PM_BR;\r
1889     case BlackQueen:\r
1890         return PM_BQ;\r
1891     case BlackKing:\r
1892         return PM_BK;\r
1893     case WhitePawn:\r
1894         return PM_WP;\r
1895     case WhiteKnight:\r
1896         return PM_WN;\r
1897     case WhiteBishop:\r
1898         return PM_WB;\r
1899     case WhiteRook:\r
1900         return PM_WR;\r
1901     case WhiteQueen:\r
1902         return PM_WQ;\r
1903     case WhiteKing:\r
1904         return PM_WK;\r
1905 \r
1906     case BlackAngel:\r
1907         return PM_BA;\r
1908     case BlackMarshall:\r
1909         return PM_BC;\r
1910     case BlackFerz:\r
1911         return PM_BF;\r
1912     case BlackNightrider:\r
1913         return PM_BH;\r
1914     case BlackAlfil:\r
1915         return PM_BE;\r
1916     case BlackWazir:\r
1917         return PM_BW;\r
1918     case BlackUnicorn:\r
1919         return PM_BU;\r
1920     case BlackCannon:\r
1921         return PM_BO;\r
1922     case BlackGrasshopper:\r
1923         return PM_BG;\r
1924     case BlackMan:\r
1925         return PM_BM;\r
1926     case BlackSilver:\r
1927         return PM_BSG;\r
1928     case BlackLance:\r
1929         return PM_BL;\r
1930     case BlackFalcon:\r
1931         return PM_BV;\r
1932     case BlackCobra:\r
1933         return PM_BS;\r
1934     case BlackCardinal:\r
1935         return PM_BAB;\r
1936     case BlackDragon:\r
1937         return PM_BD;\r
1938 \r
1939     case WhiteAngel:\r
1940         return PM_WA;\r
1941     case WhiteMarshall:\r
1942         return PM_WC;\r
1943     case WhiteFerz:\r
1944         return PM_WF;\r
1945     case WhiteNightrider:\r
1946         return PM_WH;\r
1947     case WhiteAlfil:\r
1948         return PM_WE;\r
1949     case WhiteWazir:\r
1950         return PM_WW;\r
1951     case WhiteUnicorn:\r
1952         return PM_WU;\r
1953     case WhiteCannon:\r
1954         return PM_WO;\r
1955     case WhiteGrasshopper:\r
1956         return PM_WG;\r
1957     case WhiteMan:\r
1958         return PM_WM;\r
1959     case WhiteSilver:\r
1960         return PM_WSG;\r
1961     case WhiteLance:\r
1962         return PM_WL;\r
1963     case WhiteFalcon:\r
1964         return PM_WV;\r
1965     case WhiteCobra:\r
1966         return PM_WS;\r
1967     case WhiteCardinal:\r
1968         return PM_WAB;\r
1969     case WhiteDragon:\r
1970         return PM_WD;\r
1971     }\r
1972 \r
1973     return 0;\r
1974 }\r
1975 \r
1976 void CreatePiecesFromFont()\r
1977 {\r
1978     LOGFONT lf;\r
1979     HDC hdc_window = NULL;\r
1980     HDC hdc = NULL;\r
1981     HFONT hfont_old;\r
1982     int fontHeight;\r
1983     int i;\r
1984 \r
1985     if( fontBitmapSquareSize < 0 ) {\r
1986         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1987         return;\r
1988     }\r
1989 \r
1990     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
1991             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1992         fontBitmapSquareSize = -1;\r
1993         return;\r
1994     }\r
1995 \r
1996     if( fontBitmapSquareSize != squareSize ) {\r
1997         hdc_window = GetDC( hwndMain );\r
1998         hdc = CreateCompatibleDC( hdc_window );\r
1999 \r
2000         if( hPieceFont != NULL ) {\r
2001             DeleteObject( hPieceFont );\r
2002         }\r
2003         else {\r
2004             for( i=0; i<=(int)BlackKing; i++ ) {\r
2005                 hPieceMask[i] = NULL;\r
2006                 hPieceFace[i] = NULL;\r
2007             }\r
2008         }\r
2009 \r
2010         fontHeight = 75;\r
2011 \r
2012         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2013             fontHeight = appData.fontPieceSize;\r
2014         }\r
2015 \r
2016         fontHeight = (fontHeight * squareSize) / 100;\r
2017 \r
2018         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2019         lf.lfWidth = 0;\r
2020         lf.lfEscapement = 0;\r
2021         lf.lfOrientation = 0;\r
2022         lf.lfWeight = FW_NORMAL;\r
2023         lf.lfItalic = 0;\r
2024         lf.lfUnderline = 0;\r
2025         lf.lfStrikeOut = 0;\r
2026         lf.lfCharSet = DEFAULT_CHARSET;\r
2027         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2028         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2029         lf.lfQuality = PROOF_QUALITY;\r
2030         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2031         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2032         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2033 \r
2034         hPieceFont = CreateFontIndirect( &lf );\r
2035 \r
2036         if( hPieceFont == NULL ) {\r
2037             fontBitmapSquareSize = -2;\r
2038         }\r
2039         else {\r
2040             /* Setup font-to-piece character table */\r
2041             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2042                 /* No (or wrong) global settings, try to detect the font */\r
2043                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2044                     /* Alpha */\r
2045                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2046                 }\r
2047                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2048                     /* DiagramTT* family */\r
2049                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2050                 }\r
2051                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2052                     /* Fairy symbols */\r
2053                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2054                 }\r
2055                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2056                     /* Good Companion (Some characters get warped as literal :-( */\r
2057                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2058                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2059                     SetCharTable(pieceToFontChar, s);\r
2060                 }\r
2061                 else {\r
2062                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2063                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2064                 }\r
2065             }\r
2066 \r
2067             /* Create bitmaps */\r
2068             hfont_old = SelectObject( hdc, hPieceFont );\r
2069             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2070                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2071                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2072 \r
2073             SelectObject( hdc, hfont_old );\r
2074 \r
2075             fontBitmapSquareSize = squareSize;\r
2076         }\r
2077     }\r
2078 \r
2079     if( hdc != NULL ) {\r
2080         DeleteDC( hdc );\r
2081     }\r
2082 \r
2083     if( hdc_window != NULL ) {\r
2084         ReleaseDC( hwndMain, hdc_window );\r
2085     }\r
2086 }\r
2087 \r
2088 HBITMAP\r
2089 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2090 {\r
2091   char name[128];\r
2092 \r
2093     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2094   if (gameInfo.event &&\r
2095       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2096       strcmp(name, "k80s") == 0) {\r
2097     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2098   }\r
2099   return LoadBitmap(hinst, name);\r
2100 }\r
2101 \r
2102 \r
2103 /* Insert a color into the program's logical palette\r
2104    structure.  This code assumes the given color is\r
2105    the result of the RGB or PALETTERGB macro, and it\r
2106    knows how those macros work (which is documented).\r
2107 */\r
2108 VOID\r
2109 InsertInPalette(COLORREF color)\r
2110 {\r
2111   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2112 \r
2113   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2114     DisplayFatalError(_("Too many colors"), 0, 1);\r
2115     pLogPal->palNumEntries--;\r
2116     return;\r
2117   }\r
2118 \r
2119   pe->peFlags = (char) 0;\r
2120   pe->peRed = (char) (0xFF & color);\r
2121   pe->peGreen = (char) (0xFF & (color >> 8));\r
2122   pe->peBlue = (char) (0xFF & (color >> 16));\r
2123   return;\r
2124 }\r
2125 \r
2126 \r
2127 VOID\r
2128 InitDrawingColors()\r
2129 {\r
2130   if (pLogPal == NULL) {\r
2131     /* Allocate enough memory for a logical palette with\r
2132      * PALETTESIZE entries and set the size and version fields\r
2133      * of the logical palette structure.\r
2134      */\r
2135     pLogPal = (NPLOGPALETTE)\r
2136       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2137                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2138     pLogPal->palVersion    = 0x300;\r
2139   }\r
2140   pLogPal->palNumEntries = 0;\r
2141 \r
2142   InsertInPalette(lightSquareColor);\r
2143   InsertInPalette(darkSquareColor);\r
2144   InsertInPalette(whitePieceColor);\r
2145   InsertInPalette(blackPieceColor);\r
2146   InsertInPalette(highlightSquareColor);\r
2147   InsertInPalette(premoveHighlightColor);\r
2148 \r
2149   /*  create a logical color palette according the information\r
2150    *  in the LOGPALETTE structure.\r
2151    */\r
2152   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2153 \r
2154   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2155   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2156   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2157   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2158   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2159   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2160   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2161   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2162   /* [AS] Force rendering of the font-based pieces */\r
2163   if( fontBitmapSquareSize > 0 ) {\r
2164     fontBitmapSquareSize = 0;\r
2165   }\r
2166 }\r
2167 \r
2168 \r
2169 int\r
2170 BoardWidth(int boardSize, int n)\r
2171 { /* [HGM] argument n added to allow different width and height */\r
2172   int lineGap = sizeInfo[boardSize].lineGap;\r
2173 \r
2174   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2175       lineGap = appData.overrideLineGap;\r
2176   }\r
2177 \r
2178   return (n + 1) * lineGap +\r
2179           n * sizeInfo[boardSize].squareSize;\r
2180 }\r
2181 \r
2182 /* Respond to board resize by dragging edge */\r
2183 VOID\r
2184 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2185 {\r
2186   BoardSize newSize = NUM_SIZES - 1;\r
2187   static int recurse = 0;\r
2188   if (IsIconic(hwndMain)) return;\r
2189   if (recurse > 0) return;\r
2190   recurse++;\r
2191   while (newSize > 0) {\r
2192         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2193         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2194            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2195     newSize--;\r
2196   } \r
2197   boardSize = newSize;\r
2198   InitDrawingSizes(boardSize, flags);\r
2199   recurse--;\r
2200 }\r
2201 \r
2202 \r
2203 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2204 \r
2205 VOID\r
2206 InitDrawingSizes(BoardSize boardSize, int flags)\r
2207 {\r
2208   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2209   ChessSquare piece;\r
2210   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2211   HDC hdc;\r
2212   SIZE clockSize, messageSize;\r
2213   HFONT oldFont;\r
2214   char buf[MSG_SIZ];\r
2215   char *str;\r
2216   HMENU hmenu = GetMenu(hwndMain);\r
2217   RECT crect, wrect, oldRect;\r
2218   int offby;\r
2219   LOGBRUSH logbrush;\r
2220 \r
2221   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2222   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2223 \r
2224   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2225   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2226 \r
2227   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2228   oldRect.top = wpMain.y;\r
2229   oldRect.right = wpMain.x + wpMain.width;\r
2230   oldRect.bottom = wpMain.y + wpMain.height;\r
2231 \r
2232   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2233   smallLayout = sizeInfo[boardSize].smallLayout;\r
2234   squareSize = sizeInfo[boardSize].squareSize;\r
2235   lineGap = sizeInfo[boardSize].lineGap;\r
2236   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2237 \r
2238   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2239       lineGap = appData.overrideLineGap;\r
2240   }\r
2241 \r
2242   if (tinyLayout != oldTinyLayout) {\r
2243     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2244     if (tinyLayout) {\r
2245       style &= ~WS_SYSMENU;\r
2246       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2247                  "&Minimize\tCtrl+F4");\r
2248     } else {\r
2249       style |= WS_SYSMENU;\r
2250       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2251     }\r
2252     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2253 \r
2254     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2255       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2256         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2257     }\r
2258     DrawMenuBar(hwndMain);\r
2259   }\r
2260 \r
2261   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2262   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2263 \r
2264   /* Get text area sizes */\r
2265   hdc = GetDC(hwndMain);\r
2266   if (appData.clockMode) {\r
2267     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2268   } else {\r
2269     snprintf(buf, MSG_SIZ, _("White"));\r
2270   }\r
2271   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2272   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2273   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2274   str = _("We only care about the height here");\r
2275   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2276   SelectObject(hdc, oldFont);\r
2277   ReleaseDC(hwndMain, hdc);\r
2278 \r
2279   /* Compute where everything goes */\r
2280   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2281         /* [HGM] logo: if either logo is on, reserve space for it */\r
2282         logoHeight =  2*clockSize.cy;\r
2283         leftLogoRect.left   = OUTER_MARGIN;\r
2284         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2285         leftLogoRect.top    = OUTER_MARGIN;\r
2286         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2287 \r
2288         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2289         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2290         rightLogoRect.top    = OUTER_MARGIN;\r
2291         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2292 \r
2293 \r
2294     whiteRect.left = leftLogoRect.right;\r
2295     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2296     whiteRect.top = OUTER_MARGIN;\r
2297     whiteRect.bottom = whiteRect.top + logoHeight;\r
2298 \r
2299     blackRect.right = rightLogoRect.left;\r
2300     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2301     blackRect.top = whiteRect.top;\r
2302     blackRect.bottom = whiteRect.bottom;\r
2303   } else {\r
2304     whiteRect.left = OUTER_MARGIN;\r
2305     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2306     whiteRect.top = OUTER_MARGIN;\r
2307     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2308 \r
2309     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2310     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2311     blackRect.top = whiteRect.top;\r
2312     blackRect.bottom = whiteRect.bottom;\r
2313 \r
2314     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2315   }\r
2316 \r
2317   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2318   if (appData.showButtonBar) {\r
2319     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2320       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2321   } else {\r
2322     messageRect.right = OUTER_MARGIN + boardWidth;\r
2323   }\r
2324   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2325   messageRect.bottom = messageRect.top + messageSize.cy;\r
2326 \r
2327   boardRect.left = OUTER_MARGIN;\r
2328   boardRect.right = boardRect.left + boardWidth;\r
2329   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2330   boardRect.bottom = boardRect.top + boardHeight;\r
2331 \r
2332   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2333   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2334   oldBoardSize = boardSize;\r
2335   oldTinyLayout = tinyLayout;\r
2336   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2337   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2338     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2339   winW *= 1 + twoBoards;\r
2340   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2341   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2342   wpMain.height = winH; //       without disturbing window attachments\r
2343   GetWindowRect(hwndMain, &wrect);\r
2344   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2345                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2346 \r
2347   // [HGM] placement: let attached windows follow size change.\r
2348   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2349   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2350   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2351   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2352   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2353 \r
2354   /* compensate if menu bar wrapped */\r
2355   GetClientRect(hwndMain, &crect);\r
2356   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2357   wpMain.height += offby;\r
2358   switch (flags) {\r
2359   case WMSZ_TOPLEFT:\r
2360     SetWindowPos(hwndMain, NULL, \r
2361                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2362                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2363     break;\r
2364 \r
2365   case WMSZ_TOPRIGHT:\r
2366   case WMSZ_TOP:\r
2367     SetWindowPos(hwndMain, NULL, \r
2368                  wrect.left, wrect.bottom - wpMain.height, \r
2369                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2370     break;\r
2371 \r
2372   case WMSZ_BOTTOMLEFT:\r
2373   case WMSZ_LEFT:\r
2374     SetWindowPos(hwndMain, NULL, \r
2375                  wrect.right - wpMain.width, wrect.top, \r
2376                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2377     break;\r
2378 \r
2379   case WMSZ_BOTTOMRIGHT:\r
2380   case WMSZ_BOTTOM:\r
2381   case WMSZ_RIGHT:\r
2382   default:\r
2383     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2384                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2385     break;\r
2386   }\r
2387 \r
2388   hwndPause = NULL;\r
2389   for (i = 0; i < N_BUTTONS; i++) {\r
2390     if (buttonDesc[i].hwnd != NULL) {\r
2391       DestroyWindow(buttonDesc[i].hwnd);\r
2392       buttonDesc[i].hwnd = NULL;\r
2393     }\r
2394     if (appData.showButtonBar) {\r
2395       buttonDesc[i].hwnd =\r
2396         CreateWindow("BUTTON", buttonDesc[i].label,\r
2397                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2398                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2399                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2400                      (HMENU) buttonDesc[i].id,\r
2401                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2402       if (tinyLayout) {\r
2403         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2404                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2405                     MAKELPARAM(FALSE, 0));\r
2406       }\r
2407       if (buttonDesc[i].id == IDM_Pause)\r
2408         hwndPause = buttonDesc[i].hwnd;\r
2409       buttonDesc[i].wndproc = (WNDPROC)\r
2410         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2411     }\r
2412   }\r
2413   if (gridPen != NULL) DeleteObject(gridPen);\r
2414   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2415   if (premovePen != NULL) DeleteObject(premovePen);\r
2416   if (lineGap != 0) {\r
2417     logbrush.lbStyle = BS_SOLID;\r
2418     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2419     gridPen =\r
2420       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2421                    lineGap, &logbrush, 0, NULL);\r
2422     logbrush.lbColor = highlightSquareColor;\r
2423     highlightPen =\r
2424       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2425                    lineGap, &logbrush, 0, NULL);\r
2426 \r
2427     logbrush.lbColor = premoveHighlightColor; \r
2428     premovePen =\r
2429       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2430                    lineGap, &logbrush, 0, NULL);\r
2431 \r
2432     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2433     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2434       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2435       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2436         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2437       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2438         BOARD_WIDTH * (squareSize + lineGap);\r
2439       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2440     }\r
2441     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2442       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2443       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2444         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2445         lineGap / 2 + (i * (squareSize + lineGap));\r
2446       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2447         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2448       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2449     }\r
2450   }\r
2451 \r
2452   /* [HGM] Licensing requirement */\r
2453 #ifdef GOTHIC\r
2454   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2455 #endif\r
2456 #ifdef FALCON\r
2457   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2458 #endif\r
2459   GothicPopUp( "", VariantNormal);\r
2460 \r
2461 \r
2462 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2463 \r
2464   /* Load piece bitmaps for this board size */\r
2465   for (i=0; i<=2; i++) {\r
2466     for (piece = WhitePawn;\r
2467          (int) piece < (int) BlackPawn;\r
2468          piece = (ChessSquare) ((int) piece + 1)) {\r
2469       if (pieceBitmap[i][piece] != NULL)\r
2470         DeleteObject(pieceBitmap[i][piece]);\r
2471     }\r
2472   }\r
2473 \r
2474   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2475   // Orthodox Chess pieces\r
2476   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2477   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2478   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2479   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2480   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2481   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2482   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2483   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2484   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2485   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2486   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2487   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2488   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2489   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2490   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2491   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2492     // in Shogi, Hijack the unused Queen for Lance\r
2493     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2494     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2495     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2496   } else {\r
2497     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2498     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2499     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2500   }\r
2501 \r
2502   if(squareSize <= 72 && squareSize >= 33) { \r
2503     /* A & C are available in most sizes now */\r
2504     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2505       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2506       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2507       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2508       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2509       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2510       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2511       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2512       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2513       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2514       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2515       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2516       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2517     } else { // Smirf-like\r
2518       if(gameInfo.variant == VariantSChess) {\r
2519         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2520         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2521         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2522       } else {\r
2523         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2524         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2525         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2526       }\r
2527     }\r
2528     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2529       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2530       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2531       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2532     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2533       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2534       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2535       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2536     } else { // WinBoard standard\r
2537       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2538       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2539       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2540     }\r
2541   }\r
2542 \r
2543 \r
2544   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2545     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2546     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2547     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2548     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2549     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2550     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2551     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2552     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2553     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2554     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2555     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2556     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2557     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2558     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2559     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2560     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2561     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2562     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2563     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2564     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2565     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2566     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2567     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2568     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2569     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2570     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2571     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2572     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2573     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2574     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2575 \r
2576     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2577       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2578       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2579       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2580       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2581       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2582       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2583       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2584       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2585       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2586       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2587       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2588       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2589     } else {\r
2590       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2591       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2592       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2593       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2594       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2595       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2596       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2597       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2598       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2599       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2600       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2601       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2602     }\r
2603 \r
2604   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2605     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2606     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2607     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2608     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2609     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2610     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2611     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2612     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2613     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2614     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2615     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2616     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2617     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2618     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2619   }\r
2620 \r
2621 \r
2622   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2623   /* special Shogi support in this size */\r
2624   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2625       for (piece = WhitePawn;\r
2626            (int) piece < (int) BlackPawn;\r
2627            piece = (ChessSquare) ((int) piece + 1)) {\r
2628         if (pieceBitmap[i][piece] != NULL)\r
2629           DeleteObject(pieceBitmap[i][piece]);\r
2630       }\r
2631     }\r
2632   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2633   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2634   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2635   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2636   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2637   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2638   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2639   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2640   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2641   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2642   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2643   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2644   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2645   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2646   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2647   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2648   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2649   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2650   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2651   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2652   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2653   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2654   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2655   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2656   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2657   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2658   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2659   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2660   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2661   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2662   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2663   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2664   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2665   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2666   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2667   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2668   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2669   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2670   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2671   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2672   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2673   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2674   minorSize = 0;\r
2675   }\r
2676 }\r
2677 \r
2678 HBITMAP\r
2679 PieceBitmap(ChessSquare p, int kind)\r
2680 {\r
2681   if ((int) p >= (int) BlackPawn)\r
2682     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2683 \r
2684   return pieceBitmap[kind][(int) p];\r
2685 }\r
2686 \r
2687 /***************************************************************/\r
2688 \r
2689 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2690 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2691 /*\r
2692 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2693 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2694 */\r
2695 \r
2696 VOID\r
2697 SquareToPos(int row, int column, int * x, int * y)\r
2698 {\r
2699   if (flipView) {\r
2700     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2701     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2702   } else {\r
2703     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2704     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2705   }\r
2706 }\r
2707 \r
2708 VOID\r
2709 DrawCoordsOnDC(HDC hdc)\r
2710 {\r
2711   static char files[] = "0123456789012345678901221098765432109876543210";\r
2712   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2713   char str[2] = { NULLCHAR, NULLCHAR };\r
2714   int oldMode, oldAlign, x, y, start, i;\r
2715   HFONT oldFont;\r
2716   HBRUSH oldBrush;\r
2717 \r
2718   if (!appData.showCoords)\r
2719     return;\r
2720 \r
2721   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2722 \r
2723   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2724   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2725   oldAlign = GetTextAlign(hdc);\r
2726   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2727 \r
2728   y = boardRect.top + lineGap;\r
2729   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2730 \r
2731   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2732   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2733     str[0] = files[start + i];\r
2734     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2735     y += squareSize + lineGap;\r
2736   }\r
2737 \r
2738   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2739 \r
2740   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2741   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2742     str[0] = ranks[start + i];\r
2743     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2744     x += squareSize + lineGap;\r
2745   }    \r
2746 \r
2747   SelectObject(hdc, oldBrush);\r
2748   SetBkMode(hdc, oldMode);\r
2749   SetTextAlign(hdc, oldAlign);\r
2750   SelectObject(hdc, oldFont);\r
2751 }\r
2752 \r
2753 VOID\r
2754 DrawGridOnDC(HDC hdc)\r
2755 {\r
2756   HPEN oldPen;\r
2757  \r
2758   if (lineGap != 0) {\r
2759     oldPen = SelectObject(hdc, gridPen);\r
2760     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2761     SelectObject(hdc, oldPen);\r
2762   }\r
2763 }\r
2764 \r
2765 #define HIGHLIGHT_PEN 0\r
2766 #define PREMOVE_PEN   1\r
2767 \r
2768 VOID\r
2769 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2770 {\r
2771   int x1, y1;\r
2772   HPEN oldPen, hPen;\r
2773   if (lineGap == 0) return;\r
2774   if (flipView) {\r
2775     x1 = boardRect.left +\r
2776       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2777     y1 = boardRect.top +\r
2778       lineGap/2 + y * (squareSize + lineGap);\r
2779   } else {\r
2780     x1 = boardRect.left +\r
2781       lineGap/2 + x * (squareSize + lineGap);\r
2782     y1 = boardRect.top +\r
2783       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2784   }\r
2785   hPen = pen ? premovePen : highlightPen;\r
2786   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2787   MoveToEx(hdc, x1, y1, NULL);\r
2788   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2789   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2790   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2791   LineTo(hdc, x1, y1);\r
2792   SelectObject(hdc, oldPen);\r
2793 }\r
2794 \r
2795 VOID\r
2796 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2797 {\r
2798   int i;\r
2799   for (i=0; i<2; i++) {\r
2800     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2801       DrawHighlightOnDC(hdc, TRUE,\r
2802                         h->sq[i].x, h->sq[i].y,\r
2803                         pen);\r
2804   }\r
2805 }\r
2806 \r
2807 /* Note: sqcolor is used only in monoMode */\r
2808 /* Note that this code is largely duplicated in woptions.c,\r
2809    function DrawSampleSquare, so that needs to be updated too */\r
2810 VOID\r
2811 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2812 {\r
2813   HBITMAP oldBitmap;\r
2814   HBRUSH oldBrush;\r
2815   int tmpSize;\r
2816 \r
2817   if (appData.blindfold) return;\r
2818 \r
2819   /* [AS] Use font-based pieces if needed */\r
2820   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2821     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2822     CreatePiecesFromFont();\r
2823 \r
2824     if( fontBitmapSquareSize == squareSize ) {\r
2825         int index = TranslatePieceToFontPiece(piece);\r
2826 \r
2827         SelectObject( tmphdc, hPieceMask[ index ] );\r
2828 \r
2829       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2830         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2831       else\r
2832         BitBlt( hdc,\r
2833             x, y,\r
2834             squareSize, squareSize,\r
2835             tmphdc,\r
2836             0, 0,\r
2837             SRCAND );\r
2838 \r
2839         SelectObject( tmphdc, hPieceFace[ index ] );\r
2840 \r
2841       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2842         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2843       else\r
2844         BitBlt( hdc,\r
2845             x, y,\r
2846             squareSize, squareSize,\r
2847             tmphdc,\r
2848             0, 0,\r
2849             SRCPAINT );\r
2850 \r
2851         return;\r
2852     }\r
2853   }\r
2854 \r
2855   if (appData.monoMode) {\r
2856     SelectObject(tmphdc, PieceBitmap(piece, \r
2857       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2858     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2859            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2860   } else {\r
2861     tmpSize = squareSize;\r
2862     if(minorSize &&\r
2863         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2864          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2865       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2866       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2867       x += (squareSize - minorSize)>>1;\r
2868       y += squareSize - minorSize - 2;\r
2869       tmpSize = minorSize;\r
2870     }\r
2871     if (color || appData.allWhite ) {\r
2872       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2873       if( color )\r
2874               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2875       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2876       if(appData.upsideDown && color==flipView)\r
2877         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2878       else\r
2879         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2880       /* Use black for outline of white pieces */\r
2881       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2882       if(appData.upsideDown && color==flipView)\r
2883         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2884       else\r
2885         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2886     } else {\r
2887       /* Use square color for details of black pieces */\r
2888       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2889       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2890       if(appData.upsideDown && !flipView)\r
2891         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2892       else\r
2893         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2894     }\r
2895     SelectObject(hdc, oldBrush);\r
2896     SelectObject(tmphdc, oldBitmap);\r
2897   }\r
2898 }\r
2899 \r
2900 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2901 int GetBackTextureMode( int algo )\r
2902 {\r
2903     int result = BACK_TEXTURE_MODE_DISABLED;\r
2904 \r
2905     switch( algo ) \r
2906     {\r
2907         case BACK_TEXTURE_MODE_PLAIN:\r
2908             result = 1; /* Always use identity map */\r
2909             break;\r
2910         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2911             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2912             break;\r
2913     }\r
2914 \r
2915     return result;\r
2916 }\r
2917 \r
2918 /* \r
2919     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2920     to handle redraws cleanly (as random numbers would always be different).\r
2921 */\r
2922 VOID RebuildTextureSquareInfo()\r
2923 {\r
2924     BITMAP bi;\r
2925     int lite_w = 0;\r
2926     int lite_h = 0;\r
2927     int dark_w = 0;\r
2928     int dark_h = 0;\r
2929     int row;\r
2930     int col;\r
2931 \r
2932     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2933 \r
2934     if( liteBackTexture != NULL ) {\r
2935         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2936             lite_w = bi.bmWidth;\r
2937             lite_h = bi.bmHeight;\r
2938         }\r
2939     }\r
2940 \r
2941     if( darkBackTexture != NULL ) {\r
2942         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2943             dark_w = bi.bmWidth;\r
2944             dark_h = bi.bmHeight;\r
2945         }\r
2946     }\r
2947 \r
2948     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2949         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2950             if( (col + row) & 1 ) {\r
2951                 /* Lite square */\r
2952                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2953                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2954                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2955                   else\r
2956                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2957                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2958                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2959                   else\r
2960                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2961                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2962                 }\r
2963             }\r
2964             else {\r
2965                 /* Dark square */\r
2966                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2967                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2968                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2969                   else\r
2970                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2971                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2972                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2973                   else\r
2974                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2975                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2976                 }\r
2977             }\r
2978         }\r
2979     }\r
2980 }\r
2981 \r
2982 /* [AS] Arrow highlighting support */\r
2983 \r
2984 static double A_WIDTH = 5; /* Width of arrow body */\r
2985 \r
2986 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2987 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2988 \r
2989 static double Sqr( double x )\r
2990 {\r
2991     return x*x;\r
2992 }\r
2993 \r
2994 static int Round( double x )\r
2995 {\r
2996     return (int) (x + 0.5);\r
2997 }\r
2998 \r
2999 /* Draw an arrow between two points using current settings */\r
3000 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3001 {\r
3002     POINT arrow[7];\r
3003     double dx, dy, j, k, x, y;\r
3004 \r
3005     if( d_x == s_x ) {\r
3006         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3007 \r
3008         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3009         arrow[0].y = s_y;\r
3010 \r
3011         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3012         arrow[1].y = d_y - h;\r
3013 \r
3014         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3015         arrow[2].y = d_y - h;\r
3016 \r
3017         arrow[3].x = d_x;\r
3018         arrow[3].y = d_y;\r
3019 \r
3020         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3021         arrow[5].y = d_y - h;\r
3022 \r
3023         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3024         arrow[4].y = d_y - h;\r
3025 \r
3026         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3027         arrow[6].y = s_y;\r
3028     }\r
3029     else if( d_y == s_y ) {\r
3030         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3031 \r
3032         arrow[0].x = s_x;\r
3033         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3034 \r
3035         arrow[1].x = d_x - w;\r
3036         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3037 \r
3038         arrow[2].x = d_x - w;\r
3039         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3040 \r
3041         arrow[3].x = d_x;\r
3042         arrow[3].y = d_y;\r
3043 \r
3044         arrow[5].x = d_x - w;\r
3045         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3046 \r
3047         arrow[4].x = d_x - w;\r
3048         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3049 \r
3050         arrow[6].x = s_x;\r
3051         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3052     }\r
3053     else {\r
3054         /* [AS] Needed a lot of paper for this! :-) */\r
3055         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3056         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3057   \r
3058         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3059 \r
3060         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3061 \r
3062         x = s_x;\r
3063         y = s_y;\r
3064 \r
3065         arrow[0].x = Round(x - j);\r
3066         arrow[0].y = Round(y + j*dx);\r
3067 \r
3068         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3069         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3070 \r
3071         if( d_x > s_x ) {\r
3072             x = (double) d_x - k;\r
3073             y = (double) d_y - k*dy;\r
3074         }\r
3075         else {\r
3076             x = (double) d_x + k;\r
3077             y = (double) d_y + k*dy;\r
3078         }\r
3079 \r
3080         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3081 \r
3082         arrow[6].x = Round(x - j);\r
3083         arrow[6].y = Round(y + j*dx);\r
3084 \r
3085         arrow[2].x = Round(arrow[6].x + 2*j);\r
3086         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3087 \r
3088         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3089         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3090 \r
3091         arrow[4].x = d_x;\r
3092         arrow[4].y = d_y;\r
3093 \r
3094         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3095         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3096     }\r
3097 \r
3098     Polygon( hdc, arrow, 7 );\r
3099 }\r
3100 \r
3101 /* [AS] Draw an arrow between two squares */\r
3102 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3103 {\r
3104     int s_x, s_y, d_x, d_y;\r
3105     HPEN hpen;\r
3106     HPEN holdpen;\r
3107     HBRUSH hbrush;\r
3108     HBRUSH holdbrush;\r
3109     LOGBRUSH stLB;\r
3110 \r
3111     if( s_col == d_col && s_row == d_row ) {\r
3112         return;\r
3113     }\r
3114 \r
3115     /* Get source and destination points */\r
3116     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3117     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3118 \r
3119     if( d_y > s_y ) {\r
3120         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3121     }\r
3122     else if( d_y < s_y ) {\r
3123         d_y += squareSize / 2 + squareSize / 4;\r
3124     }\r
3125     else {\r
3126         d_y += squareSize / 2;\r
3127     }\r
3128 \r
3129     if( d_x > s_x ) {\r
3130         d_x += squareSize / 2 - squareSize / 4;\r
3131     }\r
3132     else if( d_x < s_x ) {\r
3133         d_x += squareSize / 2 + squareSize / 4;\r
3134     }\r
3135     else {\r
3136         d_x += squareSize / 2;\r
3137     }\r
3138 \r
3139     s_x += squareSize / 2;\r
3140     s_y += squareSize / 2;\r
3141 \r
3142     /* Adjust width */\r
3143     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3144 \r
3145     /* Draw */\r
3146     stLB.lbStyle = BS_SOLID;\r
3147     stLB.lbColor = appData.highlightArrowColor;\r
3148     stLB.lbHatch = 0;\r
3149 \r
3150     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3151     holdpen = SelectObject( hdc, hpen );\r
3152     hbrush = CreateBrushIndirect( &stLB );\r
3153     holdbrush = SelectObject( hdc, hbrush );\r
3154 \r
3155     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3156 \r
3157     SelectObject( hdc, holdpen );\r
3158     SelectObject( hdc, holdbrush );\r
3159     DeleteObject( hpen );\r
3160     DeleteObject( hbrush );\r
3161 }\r
3162 \r
3163 BOOL HasHighlightInfo()\r
3164 {\r
3165     BOOL result = FALSE;\r
3166 \r
3167     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3168         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3169     {\r
3170         result = TRUE;\r
3171     }\r
3172 \r
3173     return result;\r
3174 }\r
3175 \r
3176 BOOL IsDrawArrowEnabled()\r
3177 {\r
3178     BOOL result = FALSE;\r
3179 \r
3180     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3181         result = TRUE;\r
3182     }\r
3183 \r
3184     return result;\r
3185 }\r
3186 \r
3187 VOID DrawArrowHighlight( HDC hdc )\r
3188 {\r
3189     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3190         DrawArrowBetweenSquares( hdc,\r
3191             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3192             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3193     }\r
3194 }\r
3195 \r
3196 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3197 {\r
3198     HRGN result = NULL;\r
3199 \r
3200     if( HasHighlightInfo() ) {\r
3201         int x1, y1, x2, y2;\r
3202         int sx, sy, dx, dy;\r
3203 \r
3204         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3205         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3206 \r
3207         sx = MIN( x1, x2 );\r
3208         sy = MIN( y1, y2 );\r
3209         dx = MAX( x1, x2 ) + squareSize;\r
3210         dy = MAX( y1, y2 ) + squareSize;\r
3211 \r
3212         result = CreateRectRgn( sx, sy, dx, dy );\r
3213     }\r
3214 \r
3215     return result;\r
3216 }\r
3217 \r
3218 /*\r
3219     Warning: this function modifies the behavior of several other functions. \r
3220     \r
3221     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3222     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3223     repaint is scattered all over the place, which is not good for features such as\r
3224     "arrow highlighting" that require a full repaint of the board.\r
3225 \r
3226     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3227     user interaction, when speed is not so important) but especially to avoid errors\r
3228     in the displayed graphics.\r
3229 \r
3230     In such patched places, I always try refer to this function so there is a single\r
3231     place to maintain knowledge.\r
3232     \r
3233     To restore the original behavior, just return FALSE unconditionally.\r
3234 */\r
3235 BOOL IsFullRepaintPreferrable()\r
3236 {\r
3237     BOOL result = FALSE;\r
3238 \r
3239     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3240         /* Arrow may appear on the board */\r
3241         result = TRUE;\r
3242     }\r
3243 \r
3244     return result;\r
3245 }\r
3246 \r
3247 /* \r
3248     This function is called by DrawPosition to know whether a full repaint must\r
3249     be forced or not.\r
3250 \r
3251     Only DrawPosition may directly call this function, which makes use of \r
3252     some state information. Other function should call DrawPosition specifying \r
3253     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3254 */\r
3255 BOOL DrawPositionNeedsFullRepaint()\r
3256 {\r
3257     BOOL result = FALSE;\r
3258 \r
3259     /* \r
3260         Probably a slightly better policy would be to trigger a full repaint\r
3261         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3262         but animation is fast enough that it's difficult to notice.\r
3263     */\r
3264     if( animInfo.piece == EmptySquare ) {\r
3265         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3266             result = TRUE;\r
3267         }\r
3268     }\r
3269 \r
3270     return result;\r
3271 }\r
3272 \r
3273 VOID\r
3274 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3275 {\r
3276   int row, column, x, y, square_color, piece_color;\r
3277   ChessSquare piece;\r
3278   HBRUSH oldBrush;\r
3279   HDC texture_hdc = NULL;\r
3280 \r
3281   /* [AS] Initialize background textures if needed */\r
3282   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3283       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3284       if( backTextureSquareSize != squareSize \r
3285        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3286           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3287           backTextureSquareSize = squareSize;\r
3288           RebuildTextureSquareInfo();\r
3289       }\r
3290 \r
3291       texture_hdc = CreateCompatibleDC( hdc );\r
3292   }\r
3293 \r
3294   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3295     for (column = 0; column < BOARD_WIDTH; column++) {\r
3296   \r
3297       SquareToPos(row, column, &x, &y);\r
3298 \r
3299       piece = board[row][column];\r
3300 \r
3301       square_color = ((column + row) % 2) == 1;\r
3302       if( gameInfo.variant == VariantXiangqi ) {\r
3303           square_color = !InPalace(row, column);\r
3304           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3305           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3306       }\r
3307       piece_color = (int) piece < (int) BlackPawn;\r
3308 \r
3309 \r
3310       /* [HGM] holdings file: light square or black */\r
3311       if(column == BOARD_LEFT-2) {\r
3312             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3313                 square_color = 1;\r
3314             else {\r
3315                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3316                 continue;\r
3317             }\r
3318       } else\r
3319       if(column == BOARD_RGHT + 1 ) {\r
3320             if( row < gameInfo.holdingsSize )\r
3321                 square_color = 1;\r
3322             else {\r
3323                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3324                 continue;\r
3325             }\r
3326       }\r
3327       if(column == BOARD_LEFT-1 ) /* left align */\r
3328             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3329       else if( column == BOARD_RGHT) /* right align */\r
3330             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3331       else\r
3332       if (appData.monoMode) {\r
3333         if (piece == EmptySquare) {\r
3334           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3335                  square_color ? WHITENESS : BLACKNESS);\r
3336         } else {\r
3337           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3338         }\r
3339       } \r
3340       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3341           /* [AS] Draw the square using a texture bitmap */\r
3342           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3343           int r = row, c = column; // [HGM] do not flip board in flipView\r
3344           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3345 \r
3346           DrawTile( x, y, \r
3347               squareSize, squareSize, \r
3348               hdc, \r
3349               texture_hdc,\r
3350               backTextureSquareInfo[r][c].mode,\r
3351               backTextureSquareInfo[r][c].x,\r
3352               backTextureSquareInfo[r][c].y );\r
3353 \r
3354           SelectObject( texture_hdc, hbm );\r
3355 \r
3356           if (piece != EmptySquare) {\r
3357               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3358           }\r
3359       }\r
3360       else {\r
3361         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3362 \r
3363         oldBrush = SelectObject(hdc, brush );\r
3364         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3365         SelectObject(hdc, oldBrush);\r
3366         if (piece != EmptySquare)\r
3367           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3368       }\r
3369     }\r
3370   }\r
3371 \r
3372   if( texture_hdc != NULL ) {\r
3373     DeleteDC( texture_hdc );\r
3374   }\r
3375 }\r
3376 \r
3377 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3378 void fputDW(FILE *f, int x)\r
3379 {\r
3380         fputc(x     & 255, f);\r
3381         fputc(x>>8  & 255, f);\r
3382         fputc(x>>16 & 255, f);\r
3383         fputc(x>>24 & 255, f);\r
3384 }\r
3385 \r
3386 #define MAX_CLIPS 200   /* more than enough */\r
3387 \r
3388 VOID\r
3389 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3390 {\r
3391 //  HBITMAP bufferBitmap;\r
3392   BITMAP bi;\r
3393 //  RECT Rect;\r
3394   HDC tmphdc;\r
3395   HBITMAP hbm;\r
3396   int w = 100, h = 50;\r
3397 \r
3398   if(logo == NULL) {\r
3399     if(!logoHeight) return;\r
3400     FillRect( hdc, &logoRect, whitePieceBrush );\r
3401   }\r
3402 //  GetClientRect(hwndMain, &Rect);\r
3403 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3404 //                                      Rect.bottom-Rect.top+1);\r
3405   tmphdc = CreateCompatibleDC(hdc);\r
3406   hbm = SelectObject(tmphdc, logo);\r
3407   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3408             w = bi.bmWidth;\r
3409             h = bi.bmHeight;\r
3410   }\r
3411   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3412                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3413   SelectObject(tmphdc, hbm);\r
3414   DeleteDC(tmphdc);\r
3415 }\r
3416 \r
3417 VOID\r
3418 DisplayLogos()\r
3419 {\r
3420   if(logoHeight) {\r
3421         HDC hdc = GetDC(hwndMain);\r
3422         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3423         if(appData.autoLogo) {\r
3424           \r
3425           switch(gameMode) { // pick logos based on game mode\r
3426             case IcsObserving:\r
3427                 whiteLogo = second.programLogo; // ICS logo\r
3428                 blackLogo = second.programLogo;\r
3429             default:\r
3430                 break;\r
3431             case IcsPlayingWhite:\r
3432                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3433                 blackLogo = second.programLogo; // ICS logo\r
3434                 break;\r
3435             case IcsPlayingBlack:\r
3436                 whiteLogo = second.programLogo; // ICS logo\r
3437                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3438                 break;\r
3439             case TwoMachinesPlay:\r
3440                 if(first.twoMachinesColor[0] == 'b') {\r
3441                     whiteLogo = second.programLogo;\r
3442                     blackLogo = first.programLogo;\r
3443                 }\r
3444                 break;\r
3445             case MachinePlaysWhite:\r
3446                 blackLogo = userLogo;\r
3447                 break;\r
3448             case MachinePlaysBlack:\r
3449                 whiteLogo = userLogo;\r
3450                 blackLogo = first.programLogo;\r
3451           }\r
3452         }\r
3453         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3454         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3455         ReleaseDC(hwndMain, hdc);\r
3456   }\r
3457 }\r
3458 \r
3459 void\r
3460 UpdateLogos(int display)\r
3461 { // called after loading new engine(s), in tourney or from menu\r
3462   LoadLogo(&first, 0, FALSE);\r
3463   LoadLogo(&second, 1, appData.icsActive);\r
3464   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3465   if(display) DisplayLogos();\r
3466 }\r
3467 \r
3468 static HDC hdcSeek;\r
3469 \r
3470 // [HGM] seekgraph\r
3471 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3472 {\r
3473     POINT stPt;\r
3474     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3475     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3476     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3477     SelectObject( hdcSeek, hp );\r
3478 }\r
3479 \r
3480 // front-end wrapper for drawing functions to do rectangles\r
3481 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3482 {\r
3483     HPEN hp;\r
3484     RECT rc;\r
3485 \r
3486     if (hdcSeek == NULL) {\r
3487     hdcSeek = GetDC(hwndMain);\r
3488       if (!appData.monoMode) {\r
3489         SelectPalette(hdcSeek, hPal, FALSE);\r
3490         RealizePalette(hdcSeek);\r
3491       }\r
3492     }\r
3493     hp = SelectObject( hdcSeek, gridPen );\r
3494     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3495     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3496     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3497     SelectObject( hdcSeek, hp );\r
3498 }\r
3499 \r
3500 // front-end wrapper for putting text in graph\r
3501 void DrawSeekText(char *buf, int x, int y)\r
3502 {\r
3503         SIZE stSize;\r
3504         SetBkMode( hdcSeek, TRANSPARENT );\r
3505         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3506         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3507 }\r
3508 \r
3509 void DrawSeekDot(int x, int y, int color)\r
3510 {\r
3511         int square = color & 0x80;\r
3512         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3513                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3514         color &= 0x7F;\r
3515         if(square)\r
3516             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3517                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3518         else\r
3519             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3520                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3521             SelectObject(hdcSeek, oldBrush);\r
3522 }\r
3523 \r
3524 VOID\r
3525 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3526 {\r
3527   static Board lastReq[2], lastDrawn[2];\r
3528   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3529   static int lastDrawnFlipView = 0;\r
3530   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3531   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3532   HDC tmphdc;\r
3533   HDC hdcmem;\r
3534   HBITMAP bufferBitmap;\r
3535   HBITMAP oldBitmap;\r
3536   RECT Rect;\r
3537   HRGN clips[MAX_CLIPS];\r
3538   ChessSquare dragged_piece = EmptySquare;\r
3539   int nr = twoBoards*partnerUp;\r
3540 \r
3541   /* I'm undecided on this - this function figures out whether a full\r
3542    * repaint is necessary on its own, so there's no real reason to have the\r
3543    * caller tell it that.  I think this can safely be set to FALSE - but\r
3544    * if we trust the callers not to request full repaints unnessesarily, then\r
3545    * we could skip some clipping work.  In other words, only request a full\r
3546    * redraw when the majority of pieces have changed positions (ie. flip, \r
3547    * gamestart and similar)  --Hawk\r
3548    */\r
3549   Boolean fullrepaint = repaint;\r
3550 \r
3551   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3552 \r
3553   if( DrawPositionNeedsFullRepaint() ) {\r
3554       fullrepaint = TRUE;\r
3555   }\r
3556 \r
3557   if (board == NULL) {\r
3558     if (!lastReqValid[nr]) {\r
3559       return;\r
3560     }\r
3561     board = lastReq[nr];\r
3562   } else {\r
3563     CopyBoard(lastReq[nr], board);\r
3564     lastReqValid[nr] = 1;\r
3565   }\r
3566 \r
3567   if (doingSizing) {\r
3568     return;\r
3569   }\r
3570 \r
3571   if (IsIconic(hwndMain)) {\r
3572     return;\r
3573   }\r
3574 \r
3575   if (hdc == NULL) {\r
3576     hdc = GetDC(hwndMain);\r
3577     if (!appData.monoMode) {\r
3578       SelectPalette(hdc, hPal, FALSE);\r
3579       RealizePalette(hdc);\r
3580     }\r
3581     releaseDC = TRUE;\r
3582   } else {\r
3583     releaseDC = FALSE;\r
3584   }\r
3585 \r
3586   /* Create some work-DCs */\r
3587   hdcmem = CreateCompatibleDC(hdc);\r
3588   tmphdc = CreateCompatibleDC(hdc);\r
3589 \r
3590   /* If dragging is in progress, we temporarely remove the piece */\r
3591   /* [HGM] or temporarily decrease count if stacked              */\r
3592   /*       !! Moved to before board compare !!                   */\r
3593   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3594     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3595     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3596             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3597         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3598     } else \r
3599     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3600             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3601         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3602     } else \r
3603         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3604   }\r
3605 \r
3606   /* Figure out which squares need updating by comparing the \r
3607    * newest board with the last drawn board and checking if\r
3608    * flipping has changed.\r
3609    */\r
3610   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3611     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3612       for (column = 0; column < BOARD_WIDTH; column++) {\r
3613         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3614           SquareToPos(row, column, &x, &y);\r
3615           clips[num_clips++] =\r
3616             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3617         }\r
3618       }\r
3619     }\r
3620    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3621     for (i=0; i<2; i++) {\r
3622       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3623           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3624         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3625             lastDrawnHighlight.sq[i].y >= 0) {\r
3626           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3627                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3628           clips[num_clips++] =\r
3629             CreateRectRgn(x - lineGap, y - lineGap, \r
3630                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3631         }\r
3632         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3633           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3634           clips[num_clips++] =\r
3635             CreateRectRgn(x - lineGap, y - lineGap, \r
3636                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3637         }\r
3638       }\r
3639     }\r
3640     for (i=0; i<2; i++) {\r
3641       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3642           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3643         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3644             lastDrawnPremove.sq[i].y >= 0) {\r
3645           SquareToPos(lastDrawnPremove.sq[i].y,\r
3646                       lastDrawnPremove.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 (premoveHighlightInfo.sq[i].x >= 0 && \r
3652             premoveHighlightInfo.sq[i].y >= 0) {\r
3653           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3654                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3655           clips[num_clips++] =\r
3656             CreateRectRgn(x - lineGap, y - lineGap, \r
3657                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3658         }\r
3659       }\r
3660     }\r
3661    } else { // nr == 1\r
3662         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3663         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3664         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3665         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3666       for (i=0; i<2; i++) {\r
3667         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3668             partnerHighlightInfo.sq[i].y >= 0) {\r
3669           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3670                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3671           clips[num_clips++] =\r
3672             CreateRectRgn(x - lineGap, y - lineGap, \r
3673                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3674         }\r
3675         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3676             oldPartnerHighlight.sq[i].y >= 0) {\r
3677           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3678                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3679           clips[num_clips++] =\r
3680             CreateRectRgn(x - lineGap, y - lineGap, \r
3681                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3682         }\r
3683       }\r
3684    }\r
3685   } else {\r
3686     fullrepaint = TRUE;\r
3687   }\r
3688 \r
3689   /* Create a buffer bitmap - this is the actual bitmap\r
3690    * being written to.  When all the work is done, we can\r
3691    * copy it to the real DC (the screen).  This avoids\r
3692    * the problems with flickering.\r
3693    */\r
3694   GetClientRect(hwndMain, &Rect);\r
3695   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3696                                         Rect.bottom-Rect.top+1);\r
3697   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3698   if (!appData.monoMode) {\r
3699     SelectPalette(hdcmem, hPal, FALSE);\r
3700   }\r
3701 \r
3702   /* Create clips for dragging */\r
3703   if (!fullrepaint) {\r
3704     if (dragInfo.from.x >= 0) {\r
3705       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3706       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3707     }\r
3708     if (dragInfo.start.x >= 0) {\r
3709       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3710       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3711     }\r
3712     if (dragInfo.pos.x >= 0) {\r
3713       x = dragInfo.pos.x - squareSize / 2;\r
3714       y = dragInfo.pos.y - squareSize / 2;\r
3715       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3716     }\r
3717     if (dragInfo.lastpos.x >= 0) {\r
3718       x = dragInfo.lastpos.x - squareSize / 2;\r
3719       y = dragInfo.lastpos.y - squareSize / 2;\r
3720       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3721     }\r
3722   }\r
3723 \r
3724   /* Are we animating a move?  \r
3725    * If so, \r
3726    *   - remove the piece from the board (temporarely)\r
3727    *   - calculate the clipping region\r
3728    */\r
3729   if (!fullrepaint) {\r
3730     if (animInfo.piece != EmptySquare) {\r
3731       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3732       x = boardRect.left + animInfo.lastpos.x;\r
3733       y = boardRect.top + animInfo.lastpos.y;\r
3734       x2 = boardRect.left + animInfo.pos.x;\r
3735       y2 = boardRect.top + animInfo.pos.y;\r
3736       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3737       /* Slight kludge.  The real problem is that after AnimateMove is\r
3738          done, the position on the screen does not match lastDrawn.\r
3739          This currently causes trouble only on e.p. captures in\r
3740          atomic, where the piece moves to an empty square and then\r
3741          explodes.  The old and new positions both had an empty square\r
3742          at the destination, but animation has drawn a piece there and\r
3743          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3744       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3745     }\r
3746   }\r
3747 \r
3748   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3749   if (num_clips == 0)\r
3750     fullrepaint = TRUE;\r
3751 \r
3752   /* Set clipping on the memory DC */\r
3753   if (!fullrepaint) {\r
3754     SelectClipRgn(hdcmem, clips[0]);\r
3755     for (x = 1; x < num_clips; x++) {\r
3756       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3757         abort();  // this should never ever happen!\r
3758     }\r
3759   }\r
3760 \r
3761   /* Do all the drawing to the memory DC */\r
3762   if(explodeInfo.radius) { // [HGM] atomic\r
3763         HBRUSH oldBrush;\r
3764         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3765         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3766         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3767         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3768         x += squareSize/2;\r
3769         y += squareSize/2;\r
3770         if(!fullrepaint) {\r
3771           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3772           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3773         }\r
3774         DrawGridOnDC(hdcmem);\r
3775         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3776         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3777         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3778         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3779         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3780         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3781         SelectObject(hdcmem, oldBrush);\r
3782   } else {\r
3783     DrawGridOnDC(hdcmem);\r
3784     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3785         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3786         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3787     } else {\r
3788         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3789         oldPartnerHighlight = partnerHighlightInfo;\r
3790     }\r
3791     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3792   }\r
3793   if(nr == 0) // [HGM] dual: markers only on left board\r
3794   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3795     for (column = 0; column < BOARD_WIDTH; column++) {\r
3796         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3797             HBRUSH oldBrush = SelectObject(hdcmem, \r
3798                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3799             SquareToPos(row, column, &x, &y);\r
3800             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3801                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3802             SelectObject(hdcmem, oldBrush);\r
3803         }\r
3804     }\r
3805   }\r
3806 \r
3807   if( appData.highlightMoveWithArrow ) {\r
3808     DrawArrowHighlight(hdcmem);\r
3809   }\r
3810 \r
3811   DrawCoordsOnDC(hdcmem);\r
3812 \r
3813   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3814                  /* to make sure lastDrawn contains what is actually drawn */\r
3815 \r
3816   /* Put the dragged piece back into place and draw it (out of place!) */\r
3817     if (dragged_piece != EmptySquare) {\r
3818     /* [HGM] or restack */\r
3819     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3820                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3821     else\r
3822     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3823                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3824     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3825     x = dragInfo.pos.x - squareSize / 2;\r
3826     y = dragInfo.pos.y - squareSize / 2;\r
3827     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3828                   ((int) dragInfo.piece < (int) BlackPawn), \r
3829                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3830   }   \r
3831   \r
3832   /* Put the animated piece back into place and draw it */\r
3833   if (animInfo.piece != EmptySquare) {\r
3834     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3835     x = boardRect.left + animInfo.pos.x;\r
3836     y = boardRect.top + animInfo.pos.y;\r
3837     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3838                   ((int) animInfo.piece < (int) BlackPawn),\r
3839                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3840   }\r
3841 \r
3842   /* Release the bufferBitmap by selecting in the old bitmap \r
3843    * and delete the memory DC\r
3844    */\r
3845   SelectObject(hdcmem, oldBitmap);\r
3846   DeleteDC(hdcmem);\r
3847 \r
3848   /* Set clipping on the target DC */\r
3849   if (!fullrepaint) {\r
3850     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3851         RECT rect;\r
3852         GetRgnBox(clips[x], &rect);\r
3853         DeleteObject(clips[x]);\r
3854         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3855                           rect.right + wpMain.width/2, rect.bottom);\r
3856     }\r
3857     SelectClipRgn(hdc, clips[0]);\r
3858     for (x = 1; x < num_clips; x++) {\r
3859       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3860         abort();   // this should never ever happen!\r
3861     } \r
3862   }\r
3863 \r
3864   /* Copy the new bitmap onto the screen in one go.\r
3865    * This way we avoid any flickering\r
3866    */\r
3867   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3868   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3869          boardRect.right - boardRect.left,\r
3870          boardRect.bottom - boardRect.top,\r
3871          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3872   if(saveDiagFlag) { \r
3873     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3874     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3875 \r
3876     GetObject(bufferBitmap, sizeof(b), &b);\r
3877     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3878         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3879         bih.biWidth = b.bmWidth;\r
3880         bih.biHeight = b.bmHeight;\r
3881         bih.biPlanes = 1;\r
3882         bih.biBitCount = b.bmBitsPixel;\r
3883         bih.biCompression = 0;\r
3884         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3885         bih.biXPelsPerMeter = 0;\r
3886         bih.biYPelsPerMeter = 0;\r
3887         bih.biClrUsed = 0;\r
3888         bih.biClrImportant = 0;\r
3889 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3890 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3891         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3892 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3893 \r
3894         wb = b.bmWidthBytes;\r
3895         // count colors\r
3896         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3897                 int k = ((int*) pData)[i];\r
3898                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3899                 if(j >= 16) break;\r
3900                 color[j] = k;\r
3901                 if(j >= nrColors) nrColors = j+1;\r
3902         }\r
3903         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3904                 INT p = 0;\r
3905                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3906                     for(w=0; w<(wb>>2); w+=2) {\r
3907                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3908                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3909                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3910                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3911                         pData[p++] = m | j<<4;\r
3912                     }\r
3913                     while(p&3) pData[p++] = 0;\r
3914                 }\r
3915                 fac = 3;\r
3916                 wb = ((wb+31)>>5)<<2;\r
3917         }\r
3918         // write BITMAPFILEHEADER\r
3919         fprintf(diagFile, "BM");\r
3920         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3921         fputDW(diagFile, 0);\r
3922         fputDW(diagFile, 0x36 + (fac?64:0));\r
3923         // write BITMAPINFOHEADER\r
3924         fputDW(diagFile, 40);\r
3925         fputDW(diagFile, b.bmWidth);\r
3926         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3927         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3928         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3929         fputDW(diagFile, 0);\r
3930         fputDW(diagFile, 0);\r
3931         fputDW(diagFile, 0);\r
3932         fputDW(diagFile, 0);\r
3933         fputDW(diagFile, 0);\r
3934         fputDW(diagFile, 0);\r
3935         // write color table\r
3936         if(fac)\r
3937         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3938         // write bitmap data\r
3939         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3940                 fputc(pData[i], diagFile);\r
3941         free(pData);\r
3942      }\r
3943   }\r
3944 \r
3945   SelectObject(tmphdc, oldBitmap);\r
3946 \r
3947   /* Massive cleanup */\r
3948   for (x = 0; x < num_clips; x++)\r
3949     DeleteObject(clips[x]);\r
3950 \r
3951   DeleteDC(tmphdc);\r
3952   DeleteObject(bufferBitmap);\r
3953 \r
3954   if (releaseDC) \r
3955     ReleaseDC(hwndMain, hdc);\r
3956   \r
3957   if (lastDrawnFlipView != flipView && nr == 0) {\r
3958     if (flipView)\r
3959       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3960     else\r
3961       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3962   }\r
3963 \r
3964 /*  CopyBoard(lastDrawn, board);*/\r
3965   lastDrawnHighlight = highlightInfo;\r
3966   lastDrawnPremove   = premoveHighlightInfo;\r
3967   lastDrawnFlipView = flipView;\r
3968   lastDrawnValid[nr] = 1;\r
3969 }\r
3970 \r
3971 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3972 int\r
3973 SaveDiagram(f)\r
3974      FILE *f;\r
3975 {\r
3976     saveDiagFlag = 1; diagFile = f;\r
3977     HDCDrawPosition(NULL, TRUE, NULL);\r
3978     saveDiagFlag = 0;\r
3979 \r
3980     fclose(f);\r
3981     return TRUE;\r
3982 }\r
3983 \r
3984 \r
3985 /*---------------------------------------------------------------------------*\\r
3986 | CLIENT PAINT PROCEDURE\r
3987 |   This is the main event-handler for the WM_PAINT message.\r
3988 |\r
3989 \*---------------------------------------------------------------------------*/\r
3990 VOID\r
3991 PaintProc(HWND hwnd)\r
3992 {\r
3993   HDC         hdc;\r
3994   PAINTSTRUCT ps;\r
3995   HFONT       oldFont;\r
3996 \r
3997   if((hdc = BeginPaint(hwnd, &ps))) {\r
3998     if (IsIconic(hwnd)) {\r
3999       DrawIcon(hdc, 2, 2, iconCurrent);\r
4000     } else {\r
4001       if (!appData.monoMode) {\r
4002         SelectPalette(hdc, hPal, FALSE);\r
4003         RealizePalette(hdc);\r
4004       }\r
4005       HDCDrawPosition(hdc, 1, NULL);\r
4006       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4007         flipView = !flipView; partnerUp = !partnerUp;\r
4008         HDCDrawPosition(hdc, 1, NULL);\r
4009         flipView = !flipView; partnerUp = !partnerUp;\r
4010       }\r
4011       oldFont =\r
4012         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4013       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4014                  ETO_CLIPPED|ETO_OPAQUE,\r
4015                  &messageRect, messageText, strlen(messageText), NULL);\r
4016       SelectObject(hdc, oldFont);\r
4017       DisplayBothClocks();\r
4018       DisplayLogos();\r
4019     }\r
4020     EndPaint(hwnd,&ps);\r
4021   }\r
4022 \r
4023   return;\r
4024 }\r
4025 \r
4026 \r
4027 /*\r
4028  * If the user selects on a border boundary, return -1; if off the board,\r
4029  *   return -2.  Otherwise map the event coordinate to the square.\r
4030  * The offset boardRect.left or boardRect.top must already have been\r
4031  *   subtracted from x.\r
4032  */\r
4033 int EventToSquare(x, limit)\r
4034      int x, limit;\r
4035 {\r
4036   if (x <= 0)\r
4037     return -2;\r
4038   if (x < lineGap)\r
4039     return -1;\r
4040   x -= lineGap;\r
4041   if ((x % (squareSize + lineGap)) >= squareSize)\r
4042     return -1;\r
4043   x /= (squareSize + lineGap);\r
4044     if (x >= limit)\r
4045     return -2;\r
4046   return x;\r
4047 }\r
4048 \r
4049 typedef struct {\r
4050   char piece;\r
4051   int command;\r
4052   char* name;\r
4053 } DropEnable;\r
4054 \r
4055 DropEnable dropEnables[] = {\r
4056   { 'P', DP_Pawn, N_("Pawn") },\r
4057   { 'N', DP_Knight, N_("Knight") },\r
4058   { 'B', DP_Bishop, N_("Bishop") },\r
4059   { 'R', DP_Rook, N_("Rook") },\r
4060   { 'Q', DP_Queen, N_("Queen") },\r
4061 };\r
4062 \r
4063 VOID\r
4064 SetupDropMenu(HMENU hmenu)\r
4065 {\r
4066   int i, count, enable;\r
4067   char *p;\r
4068   extern char white_holding[], black_holding[];\r
4069   char item[MSG_SIZ];\r
4070 \r
4071   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4072     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4073                dropEnables[i].piece);\r
4074     count = 0;\r
4075     while (p && *p++ == dropEnables[i].piece) count++;\r
4076       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4077     enable = count > 0 || !appData.testLegality\r
4078       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4079                       && !appData.icsActive);\r
4080     ModifyMenu(hmenu, dropEnables[i].command,\r
4081                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4082                dropEnables[i].command, item);\r
4083   }\r
4084 }\r
4085 \r
4086 void DragPieceBegin(int x, int y, Boolean instantly)\r
4087 {\r
4088       dragInfo.lastpos.x = boardRect.left + x;\r
4089       dragInfo.lastpos.y = boardRect.top + y;\r
4090       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4091       dragInfo.from.x = fromX;\r
4092       dragInfo.from.y = fromY;\r
4093       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4094       dragInfo.start = dragInfo.from;\r
4095       SetCapture(hwndMain);\r
4096 }\r
4097 \r
4098 void DragPieceEnd(int x, int y)\r
4099 {\r
4100     ReleaseCapture();\r
4101     dragInfo.start.x = dragInfo.start.y = -1;\r
4102     dragInfo.from = dragInfo.start;\r
4103     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4104 }\r
4105 \r
4106 void ChangeDragPiece(ChessSquare piece)\r
4107 {\r
4108     dragInfo.piece = piece;\r
4109 }\r
4110 \r
4111 /* Event handler for mouse messages */\r
4112 VOID\r
4113 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4114 {\r
4115   int x, y, menuNr;\r
4116   POINT pt;\r
4117   static int recursive = 0;\r
4118   HMENU hmenu;\r
4119   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4120 \r
4121   if (recursive) {\r
4122     if (message == WM_MBUTTONUP) {\r
4123       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4124          to the middle button: we simulate pressing the left button too!\r
4125          */\r
4126       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4127       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4128     }\r
4129     return;\r
4130   }\r
4131   recursive++;\r
4132   \r
4133   pt.x = LOWORD(lParam);\r
4134   pt.y = HIWORD(lParam);\r
4135   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4136   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4137   if (!flipView && y >= 0) {\r
4138     y = BOARD_HEIGHT - 1 - y;\r
4139   }\r
4140   if (flipView && x >= 0) {\r
4141     x = BOARD_WIDTH - 1 - x;\r
4142   }\r
4143 \r
4144   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4145 \r
4146   switch (message) {\r
4147   case WM_LBUTTONDOWN:\r
4148       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4149         ClockClick(flipClock); break;\r
4150       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4151         ClockClick(!flipClock); break;\r
4152       }\r
4153       dragInfo.start.x = dragInfo.start.y = -1;\r
4154       dragInfo.from = dragInfo.start;\r
4155     if(fromX == -1 && frozen) { // not sure where this is for\r
4156                 fromX = fromY = -1; \r
4157       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4158       break;\r
4159     }\r
4160       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4161       DrawPosition(TRUE, NULL);\r
4162     break;\r
4163 \r
4164   case WM_LBUTTONUP:\r
4165       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4166       DrawPosition(TRUE, NULL);\r
4167     break;\r
4168 \r
4169   case WM_MOUSEMOVE:\r
4170     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4171     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4172     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4173     if ((appData.animateDragging || appData.highlightDragging)\r
4174         && (wParam & MK_LBUTTON)\r
4175         && dragInfo.from.x >= 0) \r
4176     {\r
4177       BOOL full_repaint = FALSE;\r
4178 \r
4179       if (appData.animateDragging) {\r
4180         dragInfo.pos = pt;\r
4181       }\r
4182       if (appData.highlightDragging) {\r
4183         SetHighlights(fromX, fromY, x, y);\r
4184         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4185             full_repaint = TRUE;\r
4186         }\r
4187       }\r
4188       \r
4189       DrawPosition( full_repaint, NULL);\r
4190       \r
4191       dragInfo.lastpos = dragInfo.pos;\r
4192     }\r
4193     break;\r
4194 \r
4195   case WM_MOUSEWHEEL: // [DM]\r
4196     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4197        /* Mouse Wheel is being rolled forward\r
4198         * Play moves forward\r
4199         */\r
4200        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4201                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4202        /* Mouse Wheel is being rolled backward\r
4203         * Play moves backward\r
4204         */\r
4205        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4206                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4207     }\r
4208     break;\r
4209 \r
4210   case WM_MBUTTONUP:\r
4211   case WM_RBUTTONUP:\r
4212     ReleaseCapture();\r
4213     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4214     break;\r
4215  \r
4216   case WM_MBUTTONDOWN:\r
4217   case WM_RBUTTONDOWN:\r
4218     ErrorPopDown();\r
4219     ReleaseCapture();\r
4220     fromX = fromY = -1;\r
4221     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4222     dragInfo.start.x = dragInfo.start.y = -1;\r
4223     dragInfo.from = dragInfo.start;\r
4224     dragInfo.lastpos = dragInfo.pos;\r
4225     if (appData.highlightDragging) {\r
4226       ClearHighlights();\r
4227     }\r
4228     if(y == -2) {\r
4229       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4230       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4231           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4232       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4233           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4234       }\r
4235       break;\r
4236     }\r
4237     DrawPosition(TRUE, NULL);\r
4238 \r
4239     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4240     switch (menuNr) {\r
4241     case 0:\r
4242       if (message == WM_MBUTTONDOWN) {\r
4243         buttonCount = 3;  /* even if system didn't think so */\r
4244         if (wParam & MK_SHIFT) \r
4245           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4246         else\r
4247           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4248       } else { /* message == WM_RBUTTONDOWN */\r
4249         /* Just have one menu, on the right button.  Windows users don't\r
4250            think to try the middle one, and sometimes other software steals\r
4251            it, or it doesn't really exist. */\r
4252         if(gameInfo.variant != VariantShogi)\r
4253             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4254         else\r
4255             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4256       }\r
4257       break;\r
4258     case 2:\r
4259       SetCapture(hwndMain);
4260       break;\r
4261     case 1:\r
4262       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4263       SetupDropMenu(hmenu);\r
4264       MenuPopup(hwnd, pt, hmenu, -1);\r
4265     default:\r
4266       break;\r
4267     }\r
4268     break;\r
4269   }\r
4270 \r
4271   recursive--;\r
4272 }\r
4273 \r
4274 /* Preprocess messages for buttons in main window */\r
4275 LRESULT CALLBACK\r
4276 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4277 {\r
4278   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4279   int i, dir;\r
4280 \r
4281   for (i=0; i<N_BUTTONS; i++) {\r
4282     if (buttonDesc[i].id == id) break;\r
4283   }\r
4284   if (i == N_BUTTONS) return 0;\r
4285   switch (message) {\r
4286   case WM_KEYDOWN:\r
4287     switch (wParam) {\r
4288     case VK_LEFT:\r
4289     case VK_RIGHT:\r
4290       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4291       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4292       return TRUE;\r
4293     }\r
4294     break;\r
4295   case WM_CHAR:\r
4296     switch (wParam) {\r
4297     case '\r':\r
4298       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4299       return TRUE;\r
4300     default:\r
4301       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4302         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4303         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4304         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4305         SetFocus(h);\r
4306         SendMessage(h, WM_CHAR, wParam, lParam);\r
4307         return TRUE;\r
4308       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4309         TypeInEvent((char)wParam);\r
4310       }\r
4311       break;\r
4312     }\r
4313     break;\r
4314   }\r
4315   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4316 }\r
4317 \r
4318 /* Process messages for Promotion dialog box */\r
4319 LRESULT CALLBACK\r
4320 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4321 {\r
4322   char promoChar;\r
4323 \r
4324   switch (message) {\r
4325   case WM_INITDIALOG: /* message: initialize dialog box */\r
4326     /* Center the dialog over the application window */\r
4327     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4328     Translate(hDlg, DLG_PromotionKing);\r
4329     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4330       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4331        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4332        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4333                SW_SHOW : SW_HIDE);\r
4334     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4335     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4336        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4337          PieceToChar(WhiteAngel) != '~') ||\r
4338         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4339          PieceToChar(BlackAngel) != '~')   ) ?\r
4340                SW_SHOW : SW_HIDE);\r
4341     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4342        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4343          PieceToChar(WhiteMarshall) != '~') ||\r
4344         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4345          PieceToChar(BlackMarshall) != '~')   ) ?\r
4346                SW_SHOW : SW_HIDE);\r
4347     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4348     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4349        gameInfo.variant != VariantShogi ?\r
4350                SW_SHOW : SW_HIDE);\r
4351     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4352        gameInfo.variant != VariantShogi ?\r
4353                SW_SHOW : SW_HIDE);\r
4354     if(gameInfo.variant == VariantShogi) {\r
4355         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4356         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4357         SetWindowText(hDlg, "Promote?");\r
4358     }\r
4359     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4360        gameInfo.variant == VariantSuper ?\r
4361                SW_SHOW : SW_HIDE);\r
4362     return TRUE;\r
4363 \r
4364   case WM_COMMAND: /* message: received a command */\r
4365     switch (LOWORD(wParam)) {\r
4366     case IDCANCEL:\r
4367       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4368       ClearHighlights();\r
4369       DrawPosition(FALSE, NULL);\r
4370       return TRUE;\r
4371     case PB_King:\r
4372       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4373       break;\r
4374     case PB_Queen:\r
4375       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4376       break;\r
4377     case PB_Rook:\r
4378       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4379       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4380       break;\r
4381     case PB_Bishop:\r
4382       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4383       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4384       break;\r
4385     case PB_Chancellor:\r
4386       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4387       break;\r
4388     case PB_Archbishop:\r
4389       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4390       break;\r
4391     case PB_Knight:\r
4392       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4393       break;\r
4394     default:\r
4395       return FALSE;\r
4396     }\r
4397     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4398     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4399     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4400     fromX = fromY = -1;\r
4401     if (!appData.highlightLastMove) {\r
4402       ClearHighlights();\r
4403       DrawPosition(FALSE, NULL);\r
4404     }\r
4405     return TRUE;\r
4406   }\r
4407   return FALSE;\r
4408 }\r
4409 \r
4410 /* Pop up promotion dialog */\r
4411 VOID\r
4412 PromotionPopup(HWND hwnd)\r
4413 {\r
4414   FARPROC lpProc;\r
4415 \r
4416   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4417   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4418     hwnd, (DLGPROC)lpProc);\r
4419   FreeProcInstance(lpProc);\r
4420 }\r
4421 \r
4422 void\r
4423 PromotionPopUp()\r
4424 {\r
4425   DrawPosition(TRUE, NULL);\r
4426   PromotionPopup(hwndMain);\r
4427 }\r
4428 \r
4429 /* Toggle ShowThinking */\r
4430 VOID\r
4431 ToggleShowThinking()\r
4432 {\r
4433   appData.showThinking = !appData.showThinking;\r
4434   ShowThinkingEvent();\r
4435 }\r
4436 \r
4437 VOID\r
4438 LoadGameDialog(HWND hwnd, char* title)\r
4439 {\r
4440   UINT number = 0;\r
4441   FILE *f;\r
4442   char fileTitle[MSG_SIZ];\r
4443   f = OpenFileDialog(hwnd, "rb", "",\r
4444                      appData.oldSaveStyle ? "gam" : "pgn",\r
4445                      GAME_FILT,\r
4446                      title, &number, fileTitle, NULL);\r
4447   if (f != NULL) {\r
4448     cmailMsgLoaded = FALSE;\r
4449     if (number == 0) {\r
4450       int error = GameListBuild(f);\r
4451       if (error) {\r
4452         DisplayError(_("Cannot build game list"), error);\r
4453       } else if (!ListEmpty(&gameList) &&\r
4454                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4455         GameListPopUp(f, fileTitle);\r
4456         return;\r
4457       }\r
4458       GameListDestroy();\r
4459       number = 1;\r
4460     }\r
4461     LoadGame(f, number, fileTitle, FALSE);\r
4462   }\r
4463 }\r
4464 \r
4465 int get_term_width()\r
4466 {\r
4467     HDC hdc;\r
4468     TEXTMETRIC tm;\r
4469     RECT rc;\r
4470     HFONT hfont, hold_font;\r
4471     LOGFONT lf;\r
4472     HWND hText;\r
4473 \r
4474     if (hwndConsole)\r
4475         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4476     else\r
4477         return 79;\r
4478 \r
4479     // get the text metrics\r
4480     hdc = GetDC(hText);\r
4481     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4482     if (consoleCF.dwEffects & CFE_BOLD)\r
4483         lf.lfWeight = FW_BOLD;\r
4484     if (consoleCF.dwEffects & CFE_ITALIC)\r
4485         lf.lfItalic = TRUE;\r
4486     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4487         lf.lfStrikeOut = TRUE;\r
4488     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4489         lf.lfUnderline = TRUE;\r
4490     hfont = CreateFontIndirect(&lf);\r
4491     hold_font = SelectObject(hdc, hfont);\r
4492     GetTextMetrics(hdc, &tm);\r
4493     SelectObject(hdc, hold_font);\r
4494     DeleteObject(hfont);\r
4495     ReleaseDC(hText, hdc);\r
4496 \r
4497     // get the rectangle\r
4498     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4499 \r
4500     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4501 }\r
4502 \r
4503 void UpdateICSWidth(HWND hText)\r
4504 {\r
4505     LONG old_width, new_width;\r
4506 \r
4507     new_width = get_term_width(hText, FALSE);\r
4508     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4509     if (new_width != old_width)\r
4510     {\r
4511         ics_update_width(new_width);\r
4512         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4513     }\r
4514 }\r
4515 \r
4516 VOID\r
4517 ChangedConsoleFont()\r
4518 {\r
4519   CHARFORMAT cfmt;\r
4520   CHARRANGE tmpsel, sel;\r
4521   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4522   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4523   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4524   PARAFORMAT paraf;\r
4525 \r
4526   cfmt.cbSize = sizeof(CHARFORMAT);\r
4527   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4528     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4529                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4530   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4531    * size.  This was undocumented in the version of MSVC++ that I had\r
4532    * when I wrote the code, but is apparently documented now.\r
4533    */\r
4534   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4535   cfmt.bCharSet = f->lf.lfCharSet;\r
4536   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4537   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4538   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4539   /* Why are the following seemingly needed too? */\r
4540   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4541   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4542   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4543   tmpsel.cpMin = 0;\r
4544   tmpsel.cpMax = -1; /*999999?*/\r
4545   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4546   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4547   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4548    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4549    */\r
4550   paraf.cbSize = sizeof(paraf);\r
4551   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4552   paraf.dxStartIndent = 0;\r
4553   paraf.dxOffset = WRAP_INDENT;\r
4554   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4555   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4556   UpdateICSWidth(hText);\r
4557 }\r
4558 \r
4559 /*---------------------------------------------------------------------------*\\r
4560  *\r
4561  * Window Proc for main window\r
4562  *\r
4563 \*---------------------------------------------------------------------------*/\r
4564 \r
4565 /* Process messages for main window, etc. */\r
4566 LRESULT CALLBACK\r
4567 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4568 {\r
4569   FARPROC lpProc;\r
4570   int wmId, wmEvent;\r
4571   char *defName;\r
4572   FILE *f;\r
4573   UINT number;\r
4574   char fileTitle[MSG_SIZ];\r
4575   char buf[MSG_SIZ];\r
4576   static SnapData sd;\r
4577   static int peek=0;\r
4578 \r
4579   switch (message) {\r
4580 \r
4581   case WM_PAINT: /* message: repaint portion of window */\r
4582     PaintProc(hwnd);\r
4583     break;\r
4584 \r
4585   case WM_ERASEBKGND:\r
4586     if (IsIconic(hwnd)) {\r
4587       /* Cheat; change the message */\r
4588       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4589     } else {\r
4590       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4591     }\r
4592     break;\r
4593 \r
4594   case WM_LBUTTONDOWN:\r
4595   case WM_MBUTTONDOWN:\r
4596   case WM_RBUTTONDOWN:\r
4597   case WM_LBUTTONUP:\r
4598   case WM_MBUTTONUP:\r
4599   case WM_RBUTTONUP:\r
4600   case WM_MOUSEMOVE:\r
4601   case WM_MOUSEWHEEL:\r
4602     MouseEvent(hwnd, message, wParam, lParam);\r
4603     break;\r
4604 \r
4605   case WM_KEYUP:\r
4606     if((char)wParam == '\b') {\r
4607       ForwardEvent(); peek = 0;\r
4608     }\r
4609 \r
4610     JAWS_KBUP_NAVIGATION\r
4611 \r
4612     break;\r
4613 \r
4614   case WM_KEYDOWN:\r
4615     if((char)wParam == '\b') {\r
4616       if(!peek) BackwardEvent(), peek = 1;\r
4617     }\r
4618 \r
4619     JAWS_KBDOWN_NAVIGATION\r
4620 \r
4621     break;\r
4622 \r
4623   case WM_CHAR:\r
4624     \r
4625     JAWS_ALT_INTERCEPT\r
4626 \r
4627     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4628         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4629         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4630         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4631         SetFocus(h);\r
4632         SendMessage(h, message, wParam, lParam);\r
4633     } else if(lParam != KF_REPEAT) {\r
4634         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4635                 TypeInEvent((char)wParam);\r
4636         } else if((char)wParam == 003) CopyGameToClipboard();\r
4637          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4638     }\r
4639 \r
4640     break;\r
4641 \r
4642   case WM_PALETTECHANGED:\r
4643     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4644       int nnew;\r
4645       HDC hdc = GetDC(hwndMain);\r
4646       SelectPalette(hdc, hPal, TRUE);\r
4647       nnew = RealizePalette(hdc);\r
4648       if (nnew > 0) {\r
4649         paletteChanged = TRUE;\r
4650         InvalidateRect(hwnd, &boardRect, FALSE);\r
4651       }\r
4652       ReleaseDC(hwnd, hdc);\r
4653     }\r
4654     break;\r
4655 \r
4656   case WM_QUERYNEWPALETTE:\r
4657     if (!appData.monoMode /*&& paletteChanged*/) {\r
4658       int nnew;\r
4659       HDC hdc = GetDC(hwndMain);\r
4660       paletteChanged = FALSE;\r
4661       SelectPalette(hdc, hPal, FALSE);\r
4662       nnew = RealizePalette(hdc);\r
4663       if (nnew > 0) {\r
4664         InvalidateRect(hwnd, &boardRect, FALSE);\r
4665       }\r
4666       ReleaseDC(hwnd, hdc);\r
4667       return TRUE;\r
4668     }\r
4669     return FALSE;\r
4670 \r
4671   case WM_COMMAND: /* message: command from application menu */\r
4672     wmId    = LOWORD(wParam);\r
4673     wmEvent = HIWORD(wParam);\r
4674 \r
4675     switch (wmId) {\r
4676     case IDM_NewGame:\r
4677       ResetGameEvent();\r
4678       SAY("new game enter a move to play against the computer with white");\r
4679       break;\r
4680 \r
4681     case IDM_NewGameFRC:\r
4682       if( NewGameFRC() == 0 ) {\r
4683         ResetGameEvent();\r
4684       }\r
4685       break;\r
4686 \r
4687     case IDM_NewVariant:\r
4688       NewVariantPopup(hwnd);\r
4689       break;\r
4690 \r
4691     case IDM_LoadGame:\r
4692       LoadGameDialog(hwnd, _("Load Game from File"));\r
4693       break;\r
4694 \r
4695     case IDM_LoadNextGame:\r
4696       ReloadGame(1);\r
4697       break;\r
4698 \r
4699     case IDM_LoadPrevGame:\r
4700       ReloadGame(-1);\r
4701       break;\r
4702 \r
4703     case IDM_ReloadGame:\r
4704       ReloadGame(0);\r
4705       break;\r
4706 \r
4707     case IDM_LoadPosition:\r
4708       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4709         Reset(FALSE, TRUE);\r
4710       }\r
4711       number = 1;\r
4712       f = OpenFileDialog(hwnd, "rb", "",\r
4713                          appData.oldSaveStyle ? "pos" : "fen",\r
4714                          POSITION_FILT,\r
4715                          _("Load Position from File"), &number, fileTitle, NULL);\r
4716       if (f != NULL) {\r
4717         LoadPosition(f, number, fileTitle);\r
4718       }\r
4719       break;\r
4720 \r
4721     case IDM_LoadNextPosition:\r
4722       ReloadPosition(1);\r
4723       break;\r
4724 \r
4725     case IDM_LoadPrevPosition:\r
4726       ReloadPosition(-1);\r
4727       break;\r
4728 \r
4729     case IDM_ReloadPosition:\r
4730       ReloadPosition(0);\r
4731       break;\r
4732 \r
4733     case IDM_SaveGame:\r
4734       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4735       f = OpenFileDialog(hwnd, "a", defName,\r
4736                          appData.oldSaveStyle ? "gam" : "pgn",\r
4737                          GAME_FILT,\r
4738                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4739       if (f != NULL) {\r
4740         SaveGame(f, 0, "");\r
4741       }\r
4742       break;\r
4743 \r
4744     case IDM_SavePosition:\r
4745       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4746       f = OpenFileDialog(hwnd, "a", defName,\r
4747                          appData.oldSaveStyle ? "pos" : "fen",\r
4748                          POSITION_FILT,\r
4749                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4750       if (f != NULL) {\r
4751         SavePosition(f, 0, "");\r
4752       }\r
4753       break;\r
4754 \r
4755     case IDM_SaveDiagram:\r
4756       defName = "diagram";\r
4757       f = OpenFileDialog(hwnd, "wb", defName,\r
4758                          "bmp",\r
4759                          DIAGRAM_FILT,\r
4760                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4761       if (f != NULL) {\r
4762         SaveDiagram(f);\r
4763       }\r
4764       break;\r
4765 \r
4766     case IDM_CopyGame:\r
4767       CopyGameToClipboard();\r
4768       break;\r
4769 \r
4770     case IDM_PasteGame:\r
4771       PasteGameFromClipboard();\r
4772       break;\r
4773 \r
4774     case IDM_CopyGameListToClipboard:\r
4775       CopyGameListToClipboard();\r
4776       break;\r
4777 \r
4778     /* [AS] Autodetect FEN or PGN data */\r
4779     case IDM_PasteAny:\r
4780       PasteGameOrFENFromClipboard();\r
4781       break;\r
4782 \r
4783     /* [AS] Move history */\r
4784     case IDM_ShowMoveHistory:\r
4785         if( MoveHistoryIsUp() ) {\r
4786             MoveHistoryPopDown();\r
4787         }\r
4788         else {\r
4789             MoveHistoryPopUp();\r
4790         }\r
4791         break;\r
4792 \r
4793     /* [AS] Eval graph */\r
4794     case IDM_ShowEvalGraph:\r
4795         if( EvalGraphIsUp() ) {\r
4796             EvalGraphPopDown();\r
4797         }\r
4798         else {\r
4799             EvalGraphPopUp();\r
4800             SetFocus(hwndMain);\r
4801         }\r
4802         break;\r
4803 \r
4804     /* [AS] Engine output */\r
4805     case IDM_ShowEngineOutput:\r
4806         if( EngineOutputIsUp() ) {\r
4807             EngineOutputPopDown();\r
4808         }\r
4809         else {\r
4810             EngineOutputPopUp();\r
4811         }\r
4812         break;\r
4813 \r
4814     /* [AS] User adjudication */\r
4815     case IDM_UserAdjudication_White:\r
4816         UserAdjudicationEvent( +1 );\r
4817         break;\r
4818 \r
4819     case IDM_UserAdjudication_Black:\r
4820         UserAdjudicationEvent( -1 );\r
4821         break;\r
4822 \r
4823     case IDM_UserAdjudication_Draw:\r
4824         UserAdjudicationEvent( 0 );\r
4825         break;\r
4826 \r
4827     /* [AS] Game list options dialog */\r
4828     case IDM_GameListOptions:\r
4829       GameListOptions();\r
4830       break;\r
4831 \r
4832     case IDM_NewChat:\r
4833       ChatPopUp(NULL);\r
4834       break;\r
4835 \r
4836     case IDM_CopyPosition:\r
4837       CopyFENToClipboard();\r
4838       break;\r
4839 \r
4840     case IDM_PastePosition:\r
4841       PasteFENFromClipboard();\r
4842       break;\r
4843 \r
4844     case IDM_MailMove:\r
4845       MailMoveEvent();\r
4846       break;\r
4847 \r
4848     case IDM_ReloadCMailMsg:\r
4849       Reset(TRUE, TRUE);\r
4850       ReloadCmailMsgEvent(FALSE);\r
4851       break;\r
4852 \r
4853     case IDM_Minimize:\r
4854       ShowWindow(hwnd, SW_MINIMIZE);\r
4855       break;\r
4856 \r
4857     case IDM_Exit:\r
4858       ExitEvent(0);\r
4859       break;\r
4860 \r
4861     case IDM_MachineWhite:\r
4862       MachineWhiteEvent();\r
4863       /*\r
4864        * refresh the tags dialog only if it's visible\r
4865        */\r
4866       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4867           char *tags;\r
4868           tags = PGNTags(&gameInfo);\r
4869           TagsPopUp(tags, CmailMsg());\r
4870           free(tags);\r
4871       }\r
4872       SAY("computer starts playing white");\r
4873       break;\r
4874 \r
4875     case IDM_MachineBlack:\r
4876       MachineBlackEvent();\r
4877       /*\r
4878        * refresh the tags dialog only if it's visible\r
4879        */\r
4880       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4881           char *tags;\r
4882           tags = PGNTags(&gameInfo);\r
4883           TagsPopUp(tags, CmailMsg());\r
4884           free(tags);\r
4885       }\r
4886       SAY("computer starts playing black");\r
4887       break;\r
4888 \r
4889     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4890       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4891       break;\r
4892 \r
4893     case IDM_TwoMachines:\r
4894       TwoMachinesEvent();\r
4895       /*\r
4896        * refresh the tags dialog only if it's visible\r
4897        */\r
4898       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4899           char *tags;\r
4900           tags = PGNTags(&gameInfo);\r
4901           TagsPopUp(tags, CmailMsg());\r
4902           free(tags);\r
4903       }\r
4904       SAY("computer starts playing both sides");\r
4905       break;\r
4906 \r
4907     case IDM_AnalysisMode:\r
4908       if (!first.analysisSupport) {\r
4909         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4910         DisplayError(buf, 0);\r
4911       } else {\r
4912         SAY("analyzing current position");\r
4913         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4914         if (appData.icsActive) {\r
4915                if (gameMode != IcsObserving) {\r
4916                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4917                        DisplayError(buf, 0);\r
4918                        /* secure check */\r
4919                        if (appData.icsEngineAnalyze) {\r
4920                                if (appData.debugMode) \r
4921                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4922                                ExitAnalyzeMode();\r
4923                                ModeHighlight();\r
4924                                break;\r
4925                        }\r
4926                        break;\r
4927                } else {\r
4928                        /* if enable, user want disable icsEngineAnalyze */\r
4929                        if (appData.icsEngineAnalyze) {\r
4930                                ExitAnalyzeMode();\r
4931                                ModeHighlight();\r
4932                                break;\r
4933                        }\r
4934                        appData.icsEngineAnalyze = TRUE;\r
4935                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4936                }\r
4937         } \r
4938         if (!appData.showThinking) ToggleShowThinking();\r
4939         AnalyzeModeEvent();\r
4940       }\r
4941       break;\r
4942 \r
4943     case IDM_AnalyzeFile:\r
4944       if (!first.analysisSupport) {\r
4945         char buf[MSG_SIZ];\r
4946           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4947         DisplayError(buf, 0);\r
4948       } else {\r
4949         if (!appData.showThinking) ToggleShowThinking();\r
4950         AnalyzeFileEvent();\r
4951 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4952         AnalysisPeriodicEvent(1);\r
4953       }\r
4954       break;\r
4955 \r
4956     case IDM_IcsClient:\r
4957       IcsClientEvent();\r
4958       break;\r
4959 \r
4960     case IDM_EditGame:\r
4961     case IDM_EditGame2:\r
4962       EditGameEvent();\r
4963       SAY("edit game");\r
4964       break;\r
4965 \r
4966     case IDM_EditPosition:\r
4967     case IDM_EditPosition2:\r
4968       EditPositionEvent();\r
4969       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4970       break;\r
4971 \r
4972     case IDM_Training:\r
4973       TrainingEvent();\r
4974       break;\r
4975 \r
4976     case IDM_ShowGameList:\r
4977       ShowGameListProc();\r
4978       break;\r
4979 \r
4980     case IDM_EditProgs1:\r
4981       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4982       break;\r
4983 \r
4984     case IDM_EditProgs2:\r
4985      LoadEnginePopUp(hwndMain);\r
4986 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4987       break;\r
4988 \r
4989     case IDM_EditServers:\r
4990       EditTagsPopUp(icsNames, &icsNames);\r
4991       break;\r
4992 \r
4993     case IDM_EditTags:\r
4994     case IDM_Tags:\r
4995       EditTagsProc();\r
4996       break;\r
4997 \r
4998     case IDM_EditBook:\r
4999       EditBookEvent();\r
5000       break;\r
5001 \r
5002     case IDM_EditComment:\r
5003     case IDM_Comment:\r
5004       if (commentUp && editComment) {\r
5005         CommentPopDown();\r
5006       } else {\r
5007         EditCommentEvent();\r
5008       }\r
5009       break;\r
5010 \r
5011     case IDM_Pause:\r
5012       PauseEvent();\r
5013       break;\r
5014 \r
5015     case IDM_Accept:\r
5016       AcceptEvent();\r
5017       break;\r
5018 \r
5019     case IDM_Decline:\r
5020       DeclineEvent();\r
5021       break;\r
5022 \r
5023     case IDM_Rematch:\r
5024       RematchEvent();\r
5025       break;\r
5026 \r
5027     case IDM_CallFlag:\r
5028       CallFlagEvent();\r
5029       break;\r
5030 \r
5031     case IDM_Draw:\r
5032       DrawEvent();\r
5033       break;\r
5034 \r
5035     case IDM_Adjourn:\r
5036       AdjournEvent();\r
5037       break;\r
5038 \r
5039     case IDM_Abort:\r
5040       AbortEvent();\r
5041       break;\r
5042 \r
5043     case IDM_Resign:\r
5044       ResignEvent();\r
5045       break;\r
5046 \r
5047     case IDM_StopObserving:\r
5048       StopObservingEvent();\r
5049       break;\r
5050 \r
5051     case IDM_StopExamining:\r
5052       StopExaminingEvent();\r
5053       break;\r
5054 \r
5055     case IDM_Upload:\r
5056       UploadGameEvent();\r
5057       break;\r
5058 \r
5059     case IDM_TypeInMove:\r
5060       TypeInEvent('\000');\r
5061       break;\r
5062 \r
5063     case IDM_TypeInName:\r
5064       PopUpNameDialog('\000');\r
5065       break;\r
5066 \r
5067     case IDM_Backward:\r
5068       BackwardEvent();\r
5069       SetFocus(hwndMain);\r
5070       break;\r
5071 \r
5072     JAWS_MENU_ITEMS\r
5073 \r
5074     case IDM_Forward:\r
5075       ForwardEvent();\r
5076       SetFocus(hwndMain);\r
5077       break;\r
5078 \r
5079     case IDM_ToStart:\r
5080       ToStartEvent();\r
5081       SetFocus(hwndMain);\r
5082       break;\r
5083 \r
5084     case IDM_ToEnd:\r
5085       ToEndEvent();\r
5086       SetFocus(hwndMain);\r
5087       break;\r
5088 \r
5089     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5090     case OPT_GameListPrev:\r
5091       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5092       break;\r
5093 \r
5094     case IDM_Revert:\r
5095       RevertEvent(FALSE);\r
5096       break;\r
5097 \r
5098     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5099       RevertEvent(TRUE);\r
5100       break;\r
5101 \r
5102     case IDM_TruncateGame:\r
5103       TruncateGameEvent();\r
5104       break;\r
5105 \r
5106     case IDM_MoveNow:\r
5107       MoveNowEvent();\r
5108       break;\r
5109 \r
5110     case IDM_RetractMove:\r
5111       RetractMoveEvent();\r
5112       break;\r
5113 \r
5114     case IDM_FlipView:\r
5115       flipView = !flipView;\r
5116       DrawPosition(FALSE, NULL);\r
5117       break;\r
5118 \r
5119     case IDM_FlipClock:\r
5120       flipClock = !flipClock;\r
5121       DisplayBothClocks();\r
5122       DisplayLogos();\r
5123       break;\r
5124 \r
5125     case IDM_MuteSounds:\r
5126       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5127       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5128                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5129       break;\r
5130 \r
5131     case IDM_GeneralOptions:\r
5132       GeneralOptionsPopup(hwnd);\r
5133       DrawPosition(TRUE, NULL);\r
5134       break;\r
5135 \r
5136     case IDM_BoardOptions:\r
5137       BoardOptionsPopup(hwnd);\r
5138       break;\r
5139 \r
5140     case IDM_EnginePlayOptions:\r
5141       EnginePlayOptionsPopup(hwnd);\r
5142       break;\r
5143 \r
5144     case IDM_Engine1Options:\r
5145       EngineOptionsPopup(hwnd, &first);\r
5146       break;\r
5147 \r
5148     case IDM_Engine2Options:\r
5149       savedHwnd = hwnd;\r
5150       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5151       EngineOptionsPopup(hwnd, &second);\r
5152       break;\r
5153 \r
5154     case IDM_OptionsUCI:\r
5155       UciOptionsPopup(hwnd);\r
5156       break;\r
5157 \r
5158     case IDM_Tourney:\r
5159       TourneyPopup(hwnd);\r
5160       break;\r
5161 \r
5162     case IDM_IcsOptions:\r
5163       IcsOptionsPopup(hwnd);\r
5164       break;\r
5165 \r
5166     case IDM_Fonts:\r
5167       FontsOptionsPopup(hwnd);\r
5168       break;\r
5169 \r
5170     case IDM_Sounds:\r
5171       SoundOptionsPopup(hwnd);\r
5172       break;\r
5173 \r
5174     case IDM_CommPort:\r
5175       CommPortOptionsPopup(hwnd);\r
5176       break;\r
5177 \r
5178     case IDM_LoadOptions:\r
5179       LoadOptionsPopup(hwnd);\r
5180       break;\r
5181 \r
5182     case IDM_SaveOptions:\r
5183       SaveOptionsPopup(hwnd);\r
5184       break;\r
5185 \r
5186     case IDM_TimeControl:\r
5187       TimeControlOptionsPopup(hwnd);\r
5188       break;\r
5189 \r
5190     case IDM_SaveSettings:\r
5191       SaveSettings(settingsFileName);\r
5192       break;\r
5193 \r
5194     case IDM_SaveSettingsOnExit:\r
5195       saveSettingsOnExit = !saveSettingsOnExit;\r
5196       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5197                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5198                                          MF_CHECKED : MF_UNCHECKED));\r
5199       break;\r
5200 \r
5201     case IDM_Hint:\r
5202       HintEvent();\r
5203       break;\r
5204 \r
5205     case IDM_Book:\r
5206       BookEvent();\r
5207       break;\r
5208 \r
5209     case IDM_AboutGame:\r
5210       AboutGameEvent();\r
5211       break;\r
5212 \r
5213     case IDM_Debug:\r
5214       appData.debugMode = !appData.debugMode;\r
5215       if (appData.debugMode) {\r
5216         char dir[MSG_SIZ];\r
5217         GetCurrentDirectory(MSG_SIZ, dir);\r
5218         SetCurrentDirectory(installDir);\r
5219         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5220         SetCurrentDirectory(dir);\r
5221         setbuf(debugFP, NULL);\r
5222       } else {\r
5223         fclose(debugFP);\r
5224         debugFP = NULL;\r
5225       }\r
5226       break;\r
5227 \r
5228     case IDM_HELPCONTENTS:\r
5229       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5230           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5231           MessageBox (GetFocus(),\r
5232                     _("Unable to activate help"),\r
5233                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5234       }\r
5235       break;\r
5236 \r
5237     case IDM_HELPSEARCH:\r
5238         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5239             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5240         MessageBox (GetFocus(),\r
5241                     _("Unable to activate help"),\r
5242                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5243       }\r
5244       break;\r
5245 \r
5246     case IDM_HELPHELP:\r
5247       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5248         MessageBox (GetFocus(),\r
5249                     _("Unable to activate help"),\r
5250                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5251       }\r
5252       break;\r
5253 \r
5254     case IDM_ABOUT:\r
5255       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5256       DialogBox(hInst, \r
5257         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5258         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5259       FreeProcInstance(lpProc);\r
5260       break;\r
5261 \r
5262     case IDM_DirectCommand1:\r
5263       AskQuestionEvent(_("Direct Command"),\r
5264                        _("Send to chess program:"), "", "1");\r
5265       break;\r
5266     case IDM_DirectCommand2:\r
5267       AskQuestionEvent(_("Direct Command"),\r
5268                        _("Send to second chess program:"), "", "2");\r
5269       break;\r
5270 \r
5271     case EP_WhitePawn:\r
5272       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case EP_WhiteKnight:\r
5277       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case EP_WhiteBishop:\r
5282       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case EP_WhiteRook:\r
5287       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_WhiteQueen:\r
5292       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_WhiteFerz:\r
5297       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_WhiteWazir:\r
5302       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_WhiteAlfil:\r
5307       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_WhiteCannon:\r
5312       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_WhiteCardinal:\r
5317       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_WhiteMarshall:\r
5322       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_WhiteKing:\r
5327       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_BlackPawn:\r
5332       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_BlackKnight:\r
5337       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_BlackBishop:\r
5342       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_BlackRook:\r
5347       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_BlackQueen:\r
5352       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_BlackFerz:\r
5357       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_BlackWazir:\r
5362       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_BlackAlfil:\r
5367       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_BlackCannon:\r
5372       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_BlackCardinal:\r
5377       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_BlackMarshall:\r
5382       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_BlackKing:\r
5387       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_EmptySquare:\r
5392       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_ClearBoard:\r
5397       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_White:\r
5402       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_Black:\r
5407       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_Promote:\r
5412       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_Demote:\r
5417       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case DP_Pawn:\r
5422       DropMenuEvent(WhitePawn, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case DP_Knight:\r
5427       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case DP_Bishop:\r
5432       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case DP_Rook:\r
5437       DropMenuEvent(WhiteRook, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case DP_Queen:\r
5442       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case IDM_English:\r
5447       barbaric = 0; appData.language = "";\r
5448       TranslateMenus(0);\r
5449       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5450       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5451       lastChecked = wmId;\r
5452       break;\r
5453 \r
5454     default:\r
5455       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5456           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5457           TranslateMenus(0);\r
5458           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5459           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5460           lastChecked = wmId;\r
5461           break;\r
5462       }\r
5463       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5464     }\r
5465     break;\r
5466 \r
5467   case WM_TIMER:\r
5468     switch (wParam) {\r
5469     case CLOCK_TIMER_ID:\r
5470       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5471       clockTimerEvent = 0;\r
5472       DecrementClocks(); /* call into back end */\r
5473       break;\r
5474     case LOAD_GAME_TIMER_ID:\r
5475       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5476       loadGameTimerEvent = 0;\r
5477       AutoPlayGameLoop(); /* call into back end */\r
5478       break;\r
5479     case ANALYSIS_TIMER_ID:\r
5480       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5481                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5482         AnalysisPeriodicEvent(0);\r
5483       } else {\r
5484         KillTimer(hwnd, analysisTimerEvent);\r
5485         analysisTimerEvent = 0;\r
5486       }\r
5487       break;\r
5488     case DELAYED_TIMER_ID:\r
5489       KillTimer(hwnd, delayedTimerEvent);\r
5490       delayedTimerEvent = 0;\r
5491       delayedTimerCallback();\r
5492       break;\r
5493     }\r
5494     break;\r
5495 \r
5496   case WM_USER_Input:\r
5497     InputEvent(hwnd, message, wParam, lParam);\r
5498     break;\r
5499 \r
5500   /* [AS] Also move "attached" child windows */\r
5501   case WM_WINDOWPOSCHANGING:\r
5502 \r
5503     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5504         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5505 \r
5506         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5507             /* Window is moving */\r
5508             RECT rcMain;\r
5509 \r
5510 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5511             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5512             rcMain.right  = wpMain.x + wpMain.width;\r
5513             rcMain.top    = wpMain.y;\r
5514             rcMain.bottom = wpMain.y + wpMain.height;\r
5515             \r
5516             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5517             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5518             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5519             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5520             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5521             wpMain.x = lpwp->x;\r
5522             wpMain.y = lpwp->y;\r
5523         }\r
5524     }\r
5525     break;\r
5526 \r
5527   /* [AS] Snapping */\r
5528   case WM_ENTERSIZEMOVE:\r
5529     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5530     if (hwnd == hwndMain) {\r
5531       doingSizing = TRUE;\r
5532       lastSizing = 0;\r
5533     }\r
5534     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5535     break;\r
5536 \r
5537   case WM_SIZING:\r
5538     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5539     if (hwnd == hwndMain) {\r
5540       lastSizing = wParam;\r
5541     }\r
5542     break;\r
5543 \r
5544   case WM_MOVING:\r
5545     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5546       return OnMoving( &sd, hwnd, wParam, lParam );\r
5547 \r
5548   case WM_EXITSIZEMOVE:\r
5549     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5550     if (hwnd == hwndMain) {\r
5551       RECT client;\r
5552       doingSizing = FALSE;\r
5553       InvalidateRect(hwnd, &boardRect, FALSE);\r
5554       GetClientRect(hwnd, &client);\r
5555       ResizeBoard(client.right, client.bottom, lastSizing);\r
5556       lastSizing = 0;\r
5557       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5558     }\r
5559     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5560     break;\r
5561 \r
5562   case WM_DESTROY: /* message: window being destroyed */\r
5563     PostQuitMessage(0);\r
5564     break;\r
5565 \r
5566   case WM_CLOSE:\r
5567     if (hwnd == hwndMain) {\r
5568       ExitEvent(0);\r
5569     }\r
5570     break;\r
5571 \r
5572   default:      /* Passes it on if unprocessed */\r
5573     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5574   }\r
5575   return 0;\r
5576 }\r
5577 \r
5578 /*---------------------------------------------------------------------------*\\r
5579  *\r
5580  * Misc utility routines\r
5581  *\r
5582 \*---------------------------------------------------------------------------*/\r
5583 \r
5584 /*\r
5585  * Decent random number generator, at least not as bad as Windows\r
5586  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5587  */\r
5588 unsigned int randstate;\r
5589 \r
5590 int\r
5591 myrandom(void)\r
5592 {\r
5593   randstate = randstate * 1664525 + 1013904223;\r
5594   return (int) randstate & 0x7fffffff;\r
5595 }\r
5596 \r
5597 void\r
5598 mysrandom(unsigned int seed)\r
5599 {\r
5600   randstate = seed;\r
5601 }\r
5602 \r
5603 \r
5604 /* \r
5605  * returns TRUE if user selects a different color, FALSE otherwise \r
5606  */\r
5607 \r
5608 BOOL\r
5609 ChangeColor(HWND hwnd, COLORREF *which)\r
5610 {\r
5611   static BOOL firstTime = TRUE;\r
5612   static DWORD customColors[16];\r
5613   CHOOSECOLOR cc;\r
5614   COLORREF newcolor;\r
5615   int i;\r
5616   ColorClass ccl;\r
5617 \r
5618   if (firstTime) {\r
5619     /* Make initial colors in use available as custom colors */\r
5620     /* Should we put the compiled-in defaults here instead? */\r
5621     i = 0;\r
5622     customColors[i++] = lightSquareColor & 0xffffff;\r
5623     customColors[i++] = darkSquareColor & 0xffffff;\r
5624     customColors[i++] = whitePieceColor & 0xffffff;\r
5625     customColors[i++] = blackPieceColor & 0xffffff;\r
5626     customColors[i++] = highlightSquareColor & 0xffffff;\r
5627     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5628 \r
5629     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5630       customColors[i++] = textAttribs[ccl].color;\r
5631     }\r
5632     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5633     firstTime = FALSE;\r
5634   }\r
5635 \r
5636   cc.lStructSize = sizeof(cc);\r
5637   cc.hwndOwner = hwnd;\r
5638   cc.hInstance = NULL;\r
5639   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5640   cc.lpCustColors = (LPDWORD) customColors;\r
5641   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5642 \r
5643   if (!ChooseColor(&cc)) return FALSE;\r
5644 \r
5645   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5646   if (newcolor == *which) return FALSE;\r
5647   *which = newcolor;\r
5648   return TRUE;\r
5649 \r
5650   /*\r
5651   InitDrawingColors();\r
5652   InvalidateRect(hwnd, &boardRect, FALSE);\r
5653   */\r
5654 }\r
5655 \r
5656 BOOLEAN\r
5657 MyLoadSound(MySound *ms)\r
5658 {\r
5659   BOOL ok = FALSE;\r
5660   struct stat st;\r
5661   FILE *f;\r
5662 \r
5663   if (ms->data && ms->flag) free(ms->data);\r
5664   ms->data = NULL;\r
5665 \r
5666   switch (ms->name[0]) {\r
5667   case NULLCHAR:\r
5668     /* Silence */\r
5669     ok = TRUE;\r
5670     break;\r
5671   case '$':\r
5672     /* System sound from Control Panel.  Don't preload here. */\r
5673     ok = TRUE;\r
5674     break;\r
5675   case '!':\r
5676     if (ms->name[1] == NULLCHAR) {\r
5677       /* "!" alone = silence */\r
5678       ok = TRUE;\r
5679     } else {\r
5680       /* Builtin wave resource.  Error if not found. */\r
5681       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5682       if (h == NULL) break;\r
5683       ms->data = (void *)LoadResource(hInst, h);\r
5684       ms->flag = 0; // not maloced, so cannot be freed!\r
5685       if (h == NULL) break;\r
5686       ok = TRUE;\r
5687     }\r
5688     break;\r
5689   default:\r
5690     /* .wav file.  Error if not found. */\r
5691     f = fopen(ms->name, "rb");\r
5692     if (f == NULL) break;\r
5693     if (fstat(fileno(f), &st) < 0) break;\r
5694     ms->data = malloc(st.st_size);\r
5695     ms->flag = 1;\r
5696     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5697     fclose(f);\r
5698     ok = TRUE;\r
5699     break;\r
5700   }\r
5701   if (!ok) {\r
5702     char buf[MSG_SIZ];\r
5703       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5704     DisplayError(buf, GetLastError());\r
5705   }\r
5706   return ok;\r
5707 }\r
5708 \r
5709 BOOLEAN\r
5710 MyPlaySound(MySound *ms)\r
5711 {\r
5712   BOOLEAN ok = FALSE;\r
5713 \r
5714   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5715   switch (ms->name[0]) {\r
5716   case NULLCHAR:\r
5717         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5718     /* Silence */\r
5719     ok = TRUE;\r
5720     break;\r
5721   case '$':\r
5722     /* System sound from Control Panel (deprecated feature).\r
5723        "$" alone or an unset sound name gets default beep (still in use). */\r
5724     if (ms->name[1]) {\r
5725       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5726     }\r
5727     if (!ok) ok = MessageBeep(MB_OK);\r
5728     break; \r
5729   case '!':\r
5730     /* Builtin wave resource, or "!" alone for silence */\r
5731     if (ms->name[1]) {\r
5732       if (ms->data == NULL) return FALSE;\r
5733       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5734     } else {\r
5735       ok = TRUE;\r
5736     }\r
5737     break;\r
5738   default:\r
5739     /* .wav file.  Error if not found. */\r
5740     if (ms->data == NULL) return FALSE;\r
5741     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5742     break;\r
5743   }\r
5744   /* Don't print an error: this can happen innocently if the sound driver\r
5745      is busy; for instance, if another instance of WinBoard is playing\r
5746      a sound at about the same time. */\r
5747   return ok;\r
5748 }\r
5749 \r
5750 \r
5751 LRESULT CALLBACK\r
5752 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5753 {\r
5754   BOOL ok;\r
5755   OPENFILENAME *ofn;\r
5756   static UINT *number; /* gross that this is static */\r
5757 \r
5758   switch (message) {\r
5759   case WM_INITDIALOG: /* message: initialize dialog box */\r
5760     /* Center the dialog over the application window */\r
5761     ofn = (OPENFILENAME *) lParam;\r
5762     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5763       number = (UINT *) ofn->lCustData;\r
5764       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5765     } else {\r
5766       number = NULL;\r
5767     }\r
5768     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5769     Translate(hDlg, 1536);\r
5770     return FALSE;  /* Allow for further processing */\r
5771 \r
5772   case WM_COMMAND:\r
5773     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5774       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5775     }\r
5776     return FALSE;  /* Allow for further processing */\r
5777   }\r
5778   return FALSE;\r
5779 }\r
5780 \r
5781 UINT APIENTRY\r
5782 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5783 {\r
5784   static UINT *number;\r
5785   OPENFILENAME *ofname;\r
5786   OFNOTIFY *ofnot;\r
5787   switch (uiMsg) {\r
5788   case WM_INITDIALOG:\r
5789     Translate(hdlg, DLG_IndexNumber);\r
5790     ofname = (OPENFILENAME *)lParam;\r
5791     number = (UINT *)(ofname->lCustData);\r
5792     break;\r
5793   case WM_NOTIFY:\r
5794     ofnot = (OFNOTIFY *)lParam;\r
5795     if (ofnot->hdr.code == CDN_FILEOK) {\r
5796       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5797     }\r
5798     break;\r
5799   }\r
5800   return 0;\r
5801 }\r
5802 \r
5803 \r
5804 FILE *\r
5805 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5806                char *nameFilt, char *dlgTitle, UINT *number,\r
5807                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5808 {\r
5809   OPENFILENAME openFileName;\r
5810   char buf1[MSG_SIZ];\r
5811   FILE *f;\r
5812 \r
5813   if (fileName == NULL) fileName = buf1;\r
5814   if (defName == NULL) {\r
5815     safeStrCpy(fileName, "*.", 3 );\r
5816     strcat(fileName, defExt);\r
5817   } else {\r
5818     safeStrCpy(fileName, defName, MSG_SIZ );\r
5819   }\r
5820     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5821   if (number) *number = 0;\r
5822 \r
5823   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5824   openFileName.hwndOwner         = hwnd;\r
5825   openFileName.hInstance         = (HANDLE) hInst;\r
5826   openFileName.lpstrFilter       = nameFilt;\r
5827   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5828   openFileName.nMaxCustFilter    = 0L;\r
5829   openFileName.nFilterIndex      = 1L;\r
5830   openFileName.lpstrFile         = fileName;\r
5831   openFileName.nMaxFile          = MSG_SIZ;\r
5832   openFileName.lpstrFileTitle    = fileTitle;\r
5833   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5834   openFileName.lpstrInitialDir   = NULL;\r
5835   openFileName.lpstrTitle        = dlgTitle;\r
5836   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5837     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5838     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5839     | (oldDialog ? 0 : OFN_EXPLORER);\r
5840   openFileName.nFileOffset       = 0;\r
5841   openFileName.nFileExtension    = 0;\r
5842   openFileName.lpstrDefExt       = defExt;\r
5843   openFileName.lCustData         = (LONG) number;\r
5844   openFileName.lpfnHook          = oldDialog ?\r
5845     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5846   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5847 \r
5848   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5849                         GetOpenFileName(&openFileName)) {\r
5850     /* open the file */\r
5851     f = fopen(openFileName.lpstrFile, write);\r
5852     if (f == NULL) {\r
5853       MessageBox(hwnd, _("File open failed"), NULL,\r
5854                  MB_OK|MB_ICONEXCLAMATION);\r
5855       return NULL;\r
5856     }\r
5857   } else {\r
5858     int err = CommDlgExtendedError();\r
5859     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5860     return FALSE;\r
5861   }\r
5862   return f;\r
5863 }\r
5864 \r
5865 \r
5866 \r
5867 VOID APIENTRY\r
5868 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5869 {\r
5870   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5871 \r
5872   /*\r
5873    * Get the first pop-up menu in the menu template. This is the\r
5874    * menu that TrackPopupMenu displays.\r
5875    */\r
5876   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5877   TranslateOneMenu(10, hmenuTrackPopup);\r
5878 \r
5879   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5880 \r
5881   /*\r
5882    * TrackPopup uses screen coordinates, so convert the\r
5883    * coordinates of the mouse click to screen coordinates.\r
5884    */\r
5885   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5886 \r
5887   /* Draw and track the floating pop-up menu. */\r
5888   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5889                  pt.x, pt.y, 0, hwnd, NULL);\r
5890 \r
5891   /* Destroy the menu.*/\r
5892   DestroyMenu(hmenu);\r
5893 }\r
5894    \r
5895 typedef struct {\r
5896   HWND hDlg, hText;\r
5897   int sizeX, sizeY, newSizeX, newSizeY;\r
5898   HDWP hdwp;\r
5899 } ResizeEditPlusButtonsClosure;\r
5900 \r
5901 BOOL CALLBACK\r
5902 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5903 {\r
5904   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5905   RECT rect;\r
5906   POINT pt;\r
5907 \r
5908   if (hChild == cl->hText) return TRUE;\r
5909   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5910   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5911   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5912   ScreenToClient(cl->hDlg, &pt);\r
5913   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5914     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5915   return TRUE;\r
5916 }\r
5917 \r
5918 /* Resize a dialog that has a (rich) edit field filling most of\r
5919    the top, with a row of buttons below */\r
5920 VOID\r
5921 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5922 {\r
5923   RECT rectText;\r
5924   int newTextHeight, newTextWidth;\r
5925   ResizeEditPlusButtonsClosure cl;\r
5926   \r
5927   /*if (IsIconic(hDlg)) return;*/\r
5928   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5929   \r
5930   cl.hdwp = BeginDeferWindowPos(8);\r
5931 \r
5932   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5933   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5934   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5935   if (newTextHeight < 0) {\r
5936     newSizeY += -newTextHeight;\r
5937     newTextHeight = 0;\r
5938   }\r
5939   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5940     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5941 \r
5942   cl.hDlg = hDlg;\r
5943   cl.hText = hText;\r
5944   cl.sizeX = sizeX;\r
5945   cl.sizeY = sizeY;\r
5946   cl.newSizeX = newSizeX;\r
5947   cl.newSizeY = newSizeY;\r
5948   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5949 \r
5950   EndDeferWindowPos(cl.hdwp);\r
5951 }\r
5952 \r
5953 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5954 {\r
5955     RECT    rChild, rParent;\r
5956     int     wChild, hChild, wParent, hParent;\r
5957     int     wScreen, hScreen, xNew, yNew;\r
5958     HDC     hdc;\r
5959 \r
5960     /* Get the Height and Width of the child window */\r
5961     GetWindowRect (hwndChild, &rChild);\r
5962     wChild = rChild.right - rChild.left;\r
5963     hChild = rChild.bottom - rChild.top;\r
5964 \r
5965     /* Get the Height and Width of the parent window */\r
5966     GetWindowRect (hwndParent, &rParent);\r
5967     wParent = rParent.right - rParent.left;\r
5968     hParent = rParent.bottom - rParent.top;\r
5969 \r
5970     /* Get the display limits */\r
5971     hdc = GetDC (hwndChild);\r
5972     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5973     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5974     ReleaseDC(hwndChild, hdc);\r
5975 \r
5976     /* Calculate new X position, then adjust for screen */\r
5977     xNew = rParent.left + ((wParent - wChild) /2);\r
5978     if (xNew < 0) {\r
5979         xNew = 0;\r
5980     } else if ((xNew+wChild) > wScreen) {\r
5981         xNew = wScreen - wChild;\r
5982     }\r
5983 \r
5984     /* Calculate new Y position, then adjust for screen */\r
5985     if( mode == 0 ) {\r
5986         yNew = rParent.top  + ((hParent - hChild) /2);\r
5987     }\r
5988     else {\r
5989         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5990     }\r
5991 \r
5992     if (yNew < 0) {\r
5993         yNew = 0;\r
5994     } else if ((yNew+hChild) > hScreen) {\r
5995         yNew = hScreen - hChild;\r
5996     }\r
5997 \r
5998     /* Set it, and return */\r
5999     return SetWindowPos (hwndChild, NULL,\r
6000                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6001 }\r
6002 \r
6003 /* Center one window over another */\r
6004 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6005 {\r
6006     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6007 }\r
6008 \r
6009 /*---------------------------------------------------------------------------*\\r
6010  *\r
6011  * Startup Dialog functions\r
6012  *\r
6013 \*---------------------------------------------------------------------------*/\r
6014 void\r
6015 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6016 {\r
6017   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6018 \r
6019   while (*cd != NULL) {\r
6020     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6021     cd++;\r
6022   }\r
6023 }\r
6024 \r
6025 void\r
6026 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6027 {\r
6028   char buf1[MAX_ARG_LEN];\r
6029   int len;\r
6030 \r
6031   if (str[0] == '@') {\r
6032     FILE* f = fopen(str + 1, "r");\r
6033     if (f == NULL) {\r
6034       DisplayFatalError(str + 1, errno, 2);\r
6035       return;\r
6036     }\r
6037     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6038     fclose(f);\r
6039     buf1[len] = NULLCHAR;\r
6040     str = buf1;\r
6041   }\r
6042 \r
6043   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6044 \r
6045   for (;;) {\r
6046     char buf[MSG_SIZ];\r
6047     char *end = strchr(str, '\n');\r
6048     if (end == NULL) return;\r
6049     memcpy(buf, str, end - str);\r
6050     buf[end - str] = NULLCHAR;\r
6051     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6052     str = end + 1;\r
6053   }\r
6054 }\r
6055 \r
6056 void\r
6057 SetStartupDialogEnables(HWND hDlg)\r
6058 {\r
6059   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6060     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6061     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6062   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6063     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6064   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6065     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6066   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6067     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6068   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6069     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6070     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6071     IsDlgButtonChecked(hDlg, OPT_View));\r
6072 }\r
6073 \r
6074 char *\r
6075 QuoteForFilename(char *filename)\r
6076 {\r
6077   int dquote, space;\r
6078   dquote = strchr(filename, '"') != NULL;\r
6079   space = strchr(filename, ' ') != NULL;\r
6080   if (dquote || space) {\r
6081     if (dquote) {\r
6082       return "'";\r
6083     } else {\r
6084       return "\"";\r
6085     }\r
6086   } else {\r
6087     return "";\r
6088   }\r
6089 }\r
6090 \r
6091 VOID\r
6092 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6093 {\r
6094   char buf[MSG_SIZ];\r
6095   char *q;\r
6096 \r
6097   InitComboStringsFromOption(hwndCombo, nthnames);\r
6098   q = QuoteForFilename(nthcp);\r
6099     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6100   if (*nthdir != NULLCHAR) {\r
6101     q = QuoteForFilename(nthdir);\r
6102       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6103   }\r
6104   if (*nthcp == NULLCHAR) {\r
6105     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6106   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6107     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6108     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6109   }\r
6110 }\r
6111 \r
6112 LRESULT CALLBACK\r
6113 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6114 {\r
6115   char buf[MSG_SIZ];\r
6116   HANDLE hwndCombo;\r
6117   char *p;\r
6118 \r
6119   switch (message) {\r
6120   case WM_INITDIALOG:\r
6121     /* Center the dialog */\r
6122     CenterWindow (hDlg, GetDesktopWindow());\r
6123     Translate(hDlg, DLG_Startup);\r
6124     /* Initialize the dialog items */\r
6125     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6126                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6127                   firstChessProgramNames);\r
6128     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6129                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6130                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6131     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6132     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6133       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6134     if (*appData.icsHelper != NULLCHAR) {\r
6135       char *q = QuoteForFilename(appData.icsHelper);\r
6136       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6137     }\r
6138     if (*appData.icsHost == NULLCHAR) {\r
6139       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6140       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6141     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6142       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6143       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6144     }\r
6145 \r
6146     if (appData.icsActive) {\r
6147       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6148     }\r
6149     else if (appData.noChessProgram) {\r
6150       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6151     }\r
6152     else {\r
6153       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6154     }\r
6155 \r
6156     SetStartupDialogEnables(hDlg);\r
6157     return TRUE;\r
6158 \r
6159   case WM_COMMAND:\r
6160     switch (LOWORD(wParam)) {\r
6161     case IDOK:\r
6162       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6163         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6164         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6165         p = buf;\r
6166         ParseArgs(StringGet, &p);\r
6167         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6168         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6169         p = buf;
6170         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6171         ParseArgs(StringGet, &p);\r
6172         SwapEngines(singleList); // ... and then make it 'second'\r
6173         appData.noChessProgram = FALSE;\r
6174         appData.icsActive = FALSE;\r
6175       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6176         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6177         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6178         p = buf;\r
6179         ParseArgs(StringGet, &p);\r
6180         if (appData.zippyPlay) {\r
6181           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6182           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6183           p = buf;\r
6184           ParseArgs(StringGet, &p);\r
6185         }\r
6186       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6187         appData.noChessProgram = TRUE;\r
6188         appData.icsActive = FALSE;\r
6189       } else {\r
6190         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6191                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6192         return TRUE;\r
6193       }\r
6194       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6195         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6196         p = buf;\r
6197         ParseArgs(StringGet, &p);\r
6198       }\r
6199       EndDialog(hDlg, TRUE);\r
6200       return TRUE;\r
6201 \r
6202     case IDCANCEL:\r
6203       ExitEvent(0);\r
6204       return TRUE;\r
6205 \r
6206     case IDM_HELPCONTENTS:\r
6207       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6208         MessageBox (GetFocus(),\r
6209                     _("Unable to activate help"),\r
6210                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6211       }\r
6212       break;\r
6213 \r
6214     default:\r
6215       SetStartupDialogEnables(hDlg);\r
6216       break;\r
6217     }\r
6218     break;\r
6219   }\r
6220   return FALSE;\r
6221 }\r
6222 \r
6223 /*---------------------------------------------------------------------------*\\r
6224  *\r
6225  * About box dialog functions\r
6226  *\r
6227 \*---------------------------------------------------------------------------*/\r
6228 \r
6229 /* Process messages for "About" dialog box */\r
6230 LRESULT CALLBACK\r
6231 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6232 {\r
6233   switch (message) {\r
6234   case WM_INITDIALOG: /* message: initialize dialog box */\r
6235     /* Center the dialog over the application window */\r
6236     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6237     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6238     Translate(hDlg, ABOUTBOX);\r
6239     JAWS_COPYRIGHT\r
6240     return (TRUE);\r
6241 \r
6242   case WM_COMMAND: /* message: received a command */\r
6243     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6244         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6245       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6246       return (TRUE);\r
6247     }\r
6248     break;\r
6249   }\r
6250   return (FALSE);\r
6251 }\r
6252 \r
6253 /*---------------------------------------------------------------------------*\\r
6254  *\r
6255  * Comment Dialog functions\r
6256  *\r
6257 \*---------------------------------------------------------------------------*/\r
6258 \r
6259 LRESULT CALLBACK\r
6260 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6261 {\r
6262   static HANDLE hwndText = NULL;\r
6263   int len, newSizeX, newSizeY, flags;\r
6264   static int sizeX, sizeY;\r
6265   char *str;\r
6266   RECT rect;\r
6267   MINMAXINFO *mmi;\r
6268 \r
6269   switch (message) {\r
6270   case WM_INITDIALOG: /* message: initialize dialog box */\r
6271     /* Initialize the dialog items */\r
6272     Translate(hDlg, DLG_EditComment);\r
6273     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6274     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6275     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6276     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6277     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6278     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6279     SetWindowText(hDlg, commentTitle);\r
6280     if (editComment) {\r
6281       SetFocus(hwndText);\r
6282     } else {\r
6283       SetFocus(GetDlgItem(hDlg, IDOK));\r
6284     }\r
6285     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6286                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6287                 MAKELPARAM(FALSE, 0));\r
6288     /* Size and position the dialog */\r
6289     if (!commentDialog) {\r
6290       commentDialog = hDlg;\r
6291       flags = SWP_NOZORDER;\r
6292       GetClientRect(hDlg, &rect);\r
6293       sizeX = rect.right;\r
6294       sizeY = rect.bottom;\r
6295       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6296           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6297         WINDOWPLACEMENT wp;\r
6298         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6299         wp.length = sizeof(WINDOWPLACEMENT);\r
6300         wp.flags = 0;\r
6301         wp.showCmd = SW_SHOW;\r
6302         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6303         wp.rcNormalPosition.left = wpComment.x;\r
6304         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6305         wp.rcNormalPosition.top = wpComment.y;\r
6306         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6307         SetWindowPlacement(hDlg, &wp);\r
6308 \r
6309         GetClientRect(hDlg, &rect);\r
6310         newSizeX = rect.right;\r
6311         newSizeY = rect.bottom;\r
6312         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6313                               newSizeX, newSizeY);\r
6314         sizeX = newSizeX;\r
6315         sizeY = newSizeY;\r
6316       }\r
6317     }\r
6318     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6319     return FALSE;\r
6320 \r
6321   case WM_COMMAND: /* message: received a command */\r
6322     switch (LOWORD(wParam)) {\r
6323     case IDOK:\r
6324       if (editComment) {\r
6325         char *p, *q;\r
6326         /* Read changed options from the dialog box */\r
6327         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6328         len = GetWindowTextLength(hwndText);\r
6329         str = (char *) malloc(len + 1);\r
6330         GetWindowText(hwndText, str, len + 1);\r
6331         p = q = str;\r
6332         while (*q) {\r
6333           if (*q == '\r')\r
6334             q++;\r
6335           else\r
6336             *p++ = *q++;\r
6337         }\r
6338         *p = NULLCHAR;\r
6339         ReplaceComment(commentIndex, str);\r
6340         free(str);\r
6341       }\r
6342       CommentPopDown();\r
6343       return TRUE;\r
6344 \r
6345     case IDCANCEL:\r
6346     case OPT_CancelComment:\r
6347       CommentPopDown();\r
6348       return TRUE;\r
6349 \r
6350     case OPT_ClearComment:\r
6351       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6352       break;\r
6353 \r
6354     case OPT_EditComment:\r
6355       EditCommentEvent();\r
6356       return TRUE;\r
6357 \r
6358     default:\r
6359       break;\r
6360     }\r
6361     break;\r
6362 \r
6363   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6364         if( wParam == OPT_CommentText ) {\r
6365             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6366 \r
6367             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6368                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6369                 POINTL pt;\r
6370                 LRESULT index;\r
6371 \r
6372                 pt.x = LOWORD( lpMF->lParam );\r
6373                 pt.y = HIWORD( lpMF->lParam );\r
6374 \r
6375                 if(lpMF->msg == WM_CHAR) {\r
6376                         CHARRANGE sel;\r
6377                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6378                         index = sel.cpMin;\r
6379                 } else\r
6380                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6381 \r
6382                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6383                 len = GetWindowTextLength(hwndText);\r
6384                 str = (char *) malloc(len + 1);\r
6385                 GetWindowText(hwndText, str, len + 1);\r
6386                 ReplaceComment(commentIndex, str);\r
6387                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6388                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6389                 free(str);\r
6390 \r
6391                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6392                 lpMF->msg = WM_USER;\r
6393 \r
6394                 return TRUE;\r
6395             }\r
6396         }\r
6397         break;\r
6398 \r
6399   case WM_SIZE:\r
6400     newSizeX = LOWORD(lParam);\r
6401     newSizeY = HIWORD(lParam);\r
6402     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6403     sizeX = newSizeX;\r
6404     sizeY = newSizeY;\r
6405     break;\r
6406 \r
6407   case WM_GETMINMAXINFO:\r
6408     /* Prevent resizing window too small */\r
6409     mmi = (MINMAXINFO *) lParam;\r
6410     mmi->ptMinTrackSize.x = 100;\r
6411     mmi->ptMinTrackSize.y = 100;\r
6412     break;\r
6413   }\r
6414   return FALSE;\r
6415 }\r
6416 \r
6417 VOID\r
6418 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6419 {\r
6420   FARPROC lpProc;\r
6421   char *p, *q;\r
6422 \r
6423   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6424 \r
6425   if (str == NULL) str = "";\r
6426   p = (char *) malloc(2 * strlen(str) + 2);\r
6427   q = p;\r
6428   while (*str) {\r
6429     if (*str == '\n') *q++ = '\r';\r
6430     *q++ = *str++;\r
6431   }\r
6432   *q = NULLCHAR;\r
6433   if (commentText != NULL) free(commentText);\r
6434 \r
6435   commentIndex = index;\r
6436   commentTitle = title;\r
6437   commentText = p;\r
6438   editComment = edit;\r
6439 \r
6440   if (commentDialog) {\r
6441     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6442     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6443   } else {\r
6444     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6445     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6446                  hwndMain, (DLGPROC)lpProc);\r
6447     FreeProcInstance(lpProc);\r
6448   }\r
6449   commentUp = TRUE;\r
6450 }\r
6451 \r
6452 \r
6453 /*---------------------------------------------------------------------------*\\r
6454  *\r
6455  * Type-in move dialog functions\r
6456  * \r
6457 \*---------------------------------------------------------------------------*/\r
6458 \r
6459 LRESULT CALLBACK\r
6460 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6461 {\r
6462   char move[MSG_SIZ];\r
6463   HWND hInput;\r
6464 \r
6465   switch (message) {\r
6466   case WM_INITDIALOG:\r
6467     move[0] = (char) lParam;\r
6468     move[1] = NULLCHAR;\r
6469     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6470     Translate(hDlg, DLG_TypeInMove);\r
6471     hInput = GetDlgItem(hDlg, OPT_Move);\r
6472     SetWindowText(hInput, move);\r
6473     SetFocus(hInput);\r
6474     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6475     return FALSE;\r
6476 \r
6477   case WM_COMMAND:\r
6478     switch (LOWORD(wParam)) {\r
6479     case IDOK:
6480 \r
6481       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6482       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6483       TypeInDoneEvent(move);\r
6484       EndDialog(hDlg, TRUE);\r
6485       return TRUE;\r
6486     case IDCANCEL:\r
6487       EndDialog(hDlg, FALSE);\r
6488       return TRUE;\r
6489     default:\r
6490       break;\r
6491     }\r
6492     break;\r
6493   }\r
6494   return FALSE;\r
6495 }\r
6496 \r
6497 VOID\r
6498 PopUpMoveDialog(char firstchar)\r
6499 {\r
6500     FARPROC lpProc;\r
6501 \r
6502       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6503       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6504         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6505       FreeProcInstance(lpProc);\r
6506 }\r
6507 \r
6508 /*---------------------------------------------------------------------------*\\r
6509  *\r
6510  * Type-in name dialog functions\r
6511  * \r
6512 \*---------------------------------------------------------------------------*/\r
6513 \r
6514 LRESULT CALLBACK\r
6515 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6516 {\r
6517   char move[MSG_SIZ];\r
6518   HWND hInput;\r
6519 \r
6520   switch (message) {\r
6521   case WM_INITDIALOG:\r
6522     move[0] = (char) lParam;\r
6523     move[1] = NULLCHAR;\r
6524     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6525     Translate(hDlg, DLG_TypeInName);\r
6526     hInput = GetDlgItem(hDlg, OPT_Name);\r
6527     SetWindowText(hInput, move);\r
6528     SetFocus(hInput);\r
6529     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6530     return FALSE;\r
6531 \r
6532   case WM_COMMAND:\r
6533     switch (LOWORD(wParam)) {\r
6534     case IDOK:\r
6535       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6536       appData.userName = strdup(move);\r
6537       SetUserLogo();\r
6538       SetGameInfo();\r
6539       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6540         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6541         DisplayTitle(move);\r
6542       }\r
6543 \r
6544 \r
6545       EndDialog(hDlg, TRUE);\r
6546       return TRUE;\r
6547     case IDCANCEL:\r
6548       EndDialog(hDlg, FALSE);\r
6549       return TRUE;\r
6550     default:\r
6551       break;\r
6552     }\r
6553     break;\r
6554   }\r
6555   return FALSE;\r
6556 }\r
6557 \r
6558 VOID\r
6559 PopUpNameDialog(char firstchar)\r
6560 {\r
6561     FARPROC lpProc;\r
6562     \r
6563       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6564       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6565         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6566       FreeProcInstance(lpProc);\r
6567 }\r
6568 \r
6569 /*---------------------------------------------------------------------------*\\r
6570  *\r
6571  *  Error dialogs\r
6572  * \r
6573 \*---------------------------------------------------------------------------*/\r
6574 \r
6575 /* Nonmodal error box */\r
6576 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6577                              WPARAM wParam, LPARAM lParam);\r
6578 \r
6579 VOID\r
6580 ErrorPopUp(char *title, char *content)\r
6581 {\r
6582   FARPROC lpProc;\r
6583   char *p, *q;\r
6584   BOOLEAN modal = hwndMain == NULL;\r
6585 \r
6586   p = content;\r
6587   q = errorMessage;\r
6588   while (*p) {\r
6589     if (*p == '\n') {\r
6590       if (modal) {\r
6591         *q++ = ' ';\r
6592         p++;\r
6593       } else {\r
6594         *q++ = '\r';\r
6595         *q++ = *p++;\r
6596       }\r
6597     } else {\r
6598       *q++ = *p++;\r
6599     }\r
6600   }\r
6601   *q = NULLCHAR;\r
6602   strncpy(errorTitle, title, sizeof(errorTitle));\r
6603   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6604   \r
6605   if (modal) {\r
6606     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6607   } else {\r
6608     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6609     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6610                  hwndMain, (DLGPROC)lpProc);\r
6611     FreeProcInstance(lpProc);\r
6612   }\r
6613 }\r
6614 \r
6615 VOID\r
6616 ErrorPopDown()\r
6617 {\r
6618   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6619   if (errorDialog == NULL) return;\r
6620   DestroyWindow(errorDialog);\r
6621   errorDialog = NULL;\r
6622   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6623 }\r
6624 \r
6625 LRESULT CALLBACK\r
6626 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6627 {\r
6628   HANDLE hwndText;\r
6629   RECT rChild;\r
6630 \r
6631   switch (message) {\r
6632   case WM_INITDIALOG:\r
6633     GetWindowRect(hDlg, &rChild);\r
6634 \r
6635     /*\r
6636     SetWindowPos(hDlg, NULL, rChild.left,\r
6637       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6638       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6639     */\r
6640 \r
6641     /* \r
6642         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6643         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6644         and it doesn't work when you resize the dialog.\r
6645         For now, just give it a default position.\r
6646     */\r
6647     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6648     Translate(hDlg, DLG_Error);\r
6649 \r
6650     errorDialog = hDlg;\r
6651     SetWindowText(hDlg, errorTitle);\r
6652     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6653     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6654     return FALSE;\r
6655 \r
6656   case WM_COMMAND:\r
6657     switch (LOWORD(wParam)) {\r
6658     case IDOK:\r
6659     case IDCANCEL:\r
6660       if (errorDialog == hDlg) errorDialog = NULL;\r
6661       DestroyWindow(hDlg);\r
6662       return TRUE;\r
6663 \r
6664     default:\r
6665       break;\r
6666     }\r
6667     break;\r
6668   }\r
6669   return FALSE;\r
6670 }\r
6671 \r
6672 #ifdef GOTHIC\r
6673 HWND gothicDialog = NULL;\r
6674 \r
6675 LRESULT CALLBACK\r
6676 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6677 {\r
6678   HANDLE hwndText;\r
6679   RECT rChild;\r
6680   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6681 \r
6682   switch (message) {\r
6683   case WM_INITDIALOG:\r
6684     GetWindowRect(hDlg, &rChild);\r
6685 \r
6686     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6687                                                              SWP_NOZORDER);\r
6688 \r
6689     /* \r
6690         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6691         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6692         and it doesn't work when you resize the dialog.\r
6693         For now, just give it a default position.\r
6694     */\r
6695     gothicDialog = hDlg;\r
6696     SetWindowText(hDlg, errorTitle);\r
6697     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6698     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6699     return FALSE;\r
6700 \r
6701   case WM_COMMAND:\r
6702     switch (LOWORD(wParam)) {\r
6703     case IDOK:\r
6704     case IDCANCEL:\r
6705       if (errorDialog == hDlg) errorDialog = NULL;\r
6706       DestroyWindow(hDlg);\r
6707       return TRUE;\r
6708 \r
6709     default:\r
6710       break;\r
6711     }\r
6712     break;\r
6713   }\r
6714   return FALSE;\r
6715 }\r
6716 \r
6717 VOID\r
6718 GothicPopUp(char *title, VariantClass variant)\r
6719 {\r
6720   FARPROC lpProc;\r
6721   static char *lastTitle;\r
6722 \r
6723   strncpy(errorTitle, title, sizeof(errorTitle));\r
6724   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6725 \r
6726   if(lastTitle != title && gothicDialog != NULL) {\r
6727     DestroyWindow(gothicDialog);\r
6728     gothicDialog = NULL;\r
6729   }\r
6730   if(variant != VariantNormal && gothicDialog == NULL) {\r
6731     title = lastTitle;\r
6732     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6733     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6734                  hwndMain, (DLGPROC)lpProc);\r
6735     FreeProcInstance(lpProc);\r
6736   }\r
6737 }\r
6738 #endif\r
6739 \r
6740 /*---------------------------------------------------------------------------*\\r
6741  *\r
6742  *  Ics Interaction console functions\r
6743  *\r
6744 \*---------------------------------------------------------------------------*/\r
6745 \r
6746 #define HISTORY_SIZE 64\r
6747 static char *history[HISTORY_SIZE];\r
6748 int histIn = 0, histP = 0;\r
6749 \r
6750 VOID\r
6751 SaveInHistory(char *cmd)\r
6752 {\r
6753   if (history[histIn] != NULL) {\r
6754     free(history[histIn]);\r
6755     history[histIn] = NULL;\r
6756   }\r
6757   if (*cmd == NULLCHAR) return;\r
6758   history[histIn] = StrSave(cmd);\r
6759   histIn = (histIn + 1) % HISTORY_SIZE;\r
6760   if (history[histIn] != NULL) {\r
6761     free(history[histIn]);\r
6762     history[histIn] = NULL;\r
6763   }\r
6764   histP = histIn;\r
6765 }\r
6766 \r
6767 char *\r
6768 PrevInHistory(char *cmd)\r
6769 {\r
6770   int newhp;\r
6771   if (histP == histIn) {\r
6772     if (history[histIn] != NULL) free(history[histIn]);\r
6773     history[histIn] = StrSave(cmd);\r
6774   }\r
6775   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6776   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6777   histP = newhp;\r
6778   return history[histP];\r
6779 }\r
6780 \r
6781 char *\r
6782 NextInHistory()\r
6783 {\r
6784   if (histP == histIn) return NULL;\r
6785   histP = (histP + 1) % HISTORY_SIZE;\r
6786   return history[histP];   \r
6787 }\r
6788 \r
6789 HMENU\r
6790 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6791 {\r
6792   HMENU hmenu, h;\r
6793   int i = 0;\r
6794   hmenu = LoadMenu(hInst, "TextMenu");\r
6795   h = GetSubMenu(hmenu, 0);\r
6796   while (e->item) {\r
6797     if (strcmp(e->item, "-") == 0) {\r
6798       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6799     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6800       int flags = MF_STRING, j = 0;\r
6801       if (e->item[0] == '|') {\r
6802         flags |= MF_MENUBARBREAK;\r
6803         j++;\r
6804       }\r
6805       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6806       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6807     }\r
6808     e++;\r
6809     i++;\r
6810   } \r
6811   return hmenu;\r
6812 }\r
6813 \r
6814 WNDPROC consoleTextWindowProc;\r
6815 \r
6816 void\r
6817 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6818 {\r
6819   char buf[MSG_SIZ], name[MSG_SIZ];\r
6820   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6821   CHARRANGE sel;\r
6822 \r
6823   if (!getname) {\r
6824     SetWindowText(hInput, command);\r
6825     if (immediate) {\r
6826       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6827     } else {\r
6828       sel.cpMin = 999999;\r
6829       sel.cpMax = 999999;\r
6830       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6831       SetFocus(hInput);\r
6832     }\r
6833     return;\r
6834   }    \r
6835   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6836   if (sel.cpMin == sel.cpMax) {\r
6837     /* Expand to surrounding word */\r
6838     TEXTRANGE tr;\r
6839     do {\r
6840       tr.chrg.cpMax = sel.cpMin;\r
6841       tr.chrg.cpMin = --sel.cpMin;\r
6842       if (sel.cpMin < 0) break;\r
6843       tr.lpstrText = name;\r
6844       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6845     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6846     sel.cpMin++;\r
6847 \r
6848     do {\r
6849       tr.chrg.cpMin = sel.cpMax;\r
6850       tr.chrg.cpMax = ++sel.cpMax;\r
6851       tr.lpstrText = name;\r
6852       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6853     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6854     sel.cpMax--;\r
6855 \r
6856     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6857       MessageBeep(MB_ICONEXCLAMATION);\r
6858       return;\r
6859     }\r
6860     tr.chrg = sel;\r
6861     tr.lpstrText = name;\r
6862     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6863   } else {\r
6864     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6865       MessageBeep(MB_ICONEXCLAMATION);\r
6866       return;\r
6867     }\r
6868     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6869   }\r
6870   if (immediate) {\r
6871     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6872     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6873     SetWindowText(hInput, buf);\r
6874     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6875   } else {\r
6876     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6877       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6878     SetWindowText(hInput, buf);\r
6879     sel.cpMin = 999999;\r
6880     sel.cpMax = 999999;\r
6881     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6882     SetFocus(hInput);\r
6883   }\r
6884 }\r
6885 \r
6886 LRESULT CALLBACK \r
6887 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6888 {\r
6889   HWND hInput;\r
6890   CHARRANGE sel;\r
6891 \r
6892   switch (message) {\r
6893   case WM_KEYDOWN:\r
6894     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6895     if(wParam=='R') return 0;\r
6896     switch (wParam) {\r
6897     case VK_PRIOR:\r
6898       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6899       return 0;\r
6900     case VK_NEXT:\r
6901       sel.cpMin = 999999;\r
6902       sel.cpMax = 999999;\r
6903       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6904       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6905       return 0;\r
6906     }\r
6907     break;\r
6908   case WM_CHAR:\r
6909    if(wParam != '\022') {\r
6910     if (wParam == '\t') {\r
6911       if (GetKeyState(VK_SHIFT) < 0) {\r
6912         /* shifted */\r
6913         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6914         if (buttonDesc[0].hwnd) {\r
6915           SetFocus(buttonDesc[0].hwnd);\r
6916         } else {\r
6917           SetFocus(hwndMain);\r
6918         }\r
6919       } else {\r
6920         /* unshifted */\r
6921         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6922       }\r
6923     } else {\r
6924       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6925       JAWS_DELETE( SetFocus(hInput); )\r
6926       SendMessage(hInput, message, wParam, lParam);\r
6927     }\r
6928     return 0;\r
6929    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6930    lParam = -1;\r
6931   case WM_RBUTTONDOWN:\r
6932     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6933       /* Move selection here if it was empty */\r
6934       POINT pt;\r
6935       pt.x = LOWORD(lParam);\r
6936       pt.y = HIWORD(lParam);\r
6937       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6938       if (sel.cpMin == sel.cpMax) {\r
6939         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6940         sel.cpMax = sel.cpMin;\r
6941         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6942       }\r
6943       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6944 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6945       POINT pt;\r
6946       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6947       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6948       if (sel.cpMin == sel.cpMax) {\r
6949         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6950         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6951       }\r
6952       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6953         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6954       }\r
6955       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6956       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6957       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6958       MenuPopup(hwnd, pt, hmenu, -1);\r
6959 }\r
6960     }\r
6961     return 0;\r
6962   case WM_RBUTTONUP:\r
6963     if (GetKeyState(VK_SHIFT) & ~1) {\r
6964       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6965         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6966     }\r
6967     return 0;\r
6968   case WM_PASTE:\r
6969     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6970     SetFocus(hInput);\r
6971     return SendMessage(hInput, message, wParam, lParam);\r
6972   case WM_MBUTTONDOWN:\r
6973     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6974   case WM_COMMAND:\r
6975     switch (LOWORD(wParam)) {\r
6976     case IDM_QuickPaste:\r
6977       {\r
6978         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6979         if (sel.cpMin == sel.cpMax) {\r
6980           MessageBeep(MB_ICONEXCLAMATION);\r
6981           return 0;\r
6982         }\r
6983         SendMessage(hwnd, WM_COPY, 0, 0);\r
6984         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6985         SendMessage(hInput, WM_PASTE, 0, 0);\r
6986         SetFocus(hInput);\r
6987         return 0;\r
6988       }\r
6989     case IDM_Cut:\r
6990       SendMessage(hwnd, WM_CUT, 0, 0);\r
6991       return 0;\r
6992     case IDM_Paste:\r
6993       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6994       return 0;\r
6995     case IDM_Copy:\r
6996       SendMessage(hwnd, WM_COPY, 0, 0);\r
6997       return 0;\r
6998     default:\r
6999       {\r
7000         int i = LOWORD(wParam) - IDM_CommandX;\r
7001         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7002             icsTextMenuEntry[i].command != NULL) {\r
7003           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7004                    icsTextMenuEntry[i].getname,\r
7005                    icsTextMenuEntry[i].immediate);\r
7006           return 0;\r
7007         }\r
7008       }\r
7009       break;\r
7010     }\r
7011     break;\r
7012   }\r
7013   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7014 }\r
7015 \r
7016 WNDPROC consoleInputWindowProc;\r
7017 \r
7018 LRESULT CALLBACK\r
7019 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7020 {\r
7021   char buf[MSG_SIZ];\r
7022   char *p;\r
7023   static BOOL sendNextChar = FALSE;\r
7024   static BOOL quoteNextChar = FALSE;\r
7025   InputSource *is = consoleInputSource;\r
7026   CHARFORMAT cf;\r
7027   CHARRANGE sel;\r
7028 \r
7029   switch (message) {\r
7030   case WM_CHAR:\r
7031     if (!appData.localLineEditing || sendNextChar) {\r
7032       is->buf[0] = (CHAR) wParam;\r
7033       is->count = 1;\r
7034       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7035       sendNextChar = FALSE;\r
7036       return 0;\r
7037     }\r
7038     if (quoteNextChar) {\r
7039       buf[0] = (char) wParam;\r
7040       buf[1] = NULLCHAR;\r
7041       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7042       quoteNextChar = FALSE;\r
7043       return 0;\r
7044     }\r
7045     switch (wParam) {\r
7046     case '\r':   /* Enter key */\r
7047       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7048       if (consoleEcho) SaveInHistory(is->buf);\r
7049       is->buf[is->count++] = '\n';\r
7050       is->buf[is->count] = NULLCHAR;\r
7051       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7052       if (consoleEcho) {\r
7053         ConsoleOutput(is->buf, is->count, TRUE);\r
7054       } else if (appData.localLineEditing) {\r
7055         ConsoleOutput("\n", 1, TRUE);\r
7056       }\r
7057       /* fall thru */\r
7058     case '\033': /* Escape key */\r
7059       SetWindowText(hwnd, "");\r
7060       cf.cbSize = sizeof(CHARFORMAT);\r
7061       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7062       if (consoleEcho) {\r
7063         cf.crTextColor = textAttribs[ColorNormal].color;\r
7064       } else {\r
7065         cf.crTextColor = COLOR_ECHOOFF;\r
7066       }\r
7067       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7068       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7069       return 0;\r
7070     case '\t':   /* Tab key */\r
7071       if (GetKeyState(VK_SHIFT) < 0) {\r
7072         /* shifted */\r
7073         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7074       } else {\r
7075         /* unshifted */\r
7076         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7077         if (buttonDesc[0].hwnd) {\r
7078           SetFocus(buttonDesc[0].hwnd);\r
7079         } else {\r
7080           SetFocus(hwndMain);\r
7081         }\r
7082       }\r
7083       return 0;\r
7084     case '\023': /* Ctrl+S */\r
7085       sendNextChar = TRUE;\r
7086       return 0;\r
7087     case '\021': /* Ctrl+Q */\r
7088       quoteNextChar = TRUE;\r
7089       return 0;\r
7090     JAWS_REPLAY\r
7091     default:\r
7092       break;\r
7093     }\r
7094     break;\r
7095   case WM_KEYDOWN:\r
7096     switch (wParam) {\r
7097     case VK_UP:\r
7098       GetWindowText(hwnd, buf, MSG_SIZ);\r
7099       p = PrevInHistory(buf);\r
7100       if (p != NULL) {\r
7101         SetWindowText(hwnd, p);\r
7102         sel.cpMin = 999999;\r
7103         sel.cpMax = 999999;\r
7104         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7105         return 0;\r
7106       }\r
7107       break;\r
7108     case VK_DOWN:\r
7109       p = NextInHistory();\r
7110       if (p != NULL) {\r
7111         SetWindowText(hwnd, p);\r
7112         sel.cpMin = 999999;\r
7113         sel.cpMax = 999999;\r
7114         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7115         return 0;\r
7116       }\r
7117       break;\r
7118     case VK_HOME:\r
7119     case VK_END:\r
7120       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7121       /* fall thru */\r
7122     case VK_PRIOR:\r
7123     case VK_NEXT:\r
7124       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7125       return 0;\r
7126     }\r
7127     break;\r
7128   case WM_MBUTTONDOWN:\r
7129     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7130       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7131     break;\r
7132   case WM_RBUTTONUP:\r
7133     if (GetKeyState(VK_SHIFT) & ~1) {\r
7134       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7135         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7136     } else {\r
7137       POINT pt;\r
7138       HMENU hmenu;\r
7139       hmenu = LoadMenu(hInst, "InputMenu");\r
7140       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7141       if (sel.cpMin == sel.cpMax) {\r
7142         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7143         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7144       }\r
7145       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7146         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7147       }\r
7148       pt.x = LOWORD(lParam);\r
7149       pt.y = HIWORD(lParam);\r
7150       MenuPopup(hwnd, pt, hmenu, -1);\r
7151     }\r
7152     return 0;\r
7153   case WM_COMMAND:\r
7154     switch (LOWORD(wParam)) { \r
7155     case IDM_Undo:\r
7156       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7157       return 0;\r
7158     case IDM_SelectAll:\r
7159       sel.cpMin = 0;\r
7160       sel.cpMax = -1; /*999999?*/\r
7161       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7162       return 0;\r
7163     case IDM_Cut:\r
7164       SendMessage(hwnd, WM_CUT, 0, 0);\r
7165       return 0;\r
7166     case IDM_Paste:\r
7167       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7168       return 0;\r
7169     case IDM_Copy:\r
7170       SendMessage(hwnd, WM_COPY, 0, 0);\r
7171       return 0;\r
7172     }\r
7173     break;\r
7174   }\r
7175   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7176 }\r
7177 \r
7178 #define CO_MAX  100000\r
7179 #define CO_TRIM   1000\r
7180 \r
7181 LRESULT CALLBACK\r
7182 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7183 {\r
7184   static SnapData sd;\r
7185   HWND hText, hInput;\r
7186   RECT rect;\r
7187   static int sizeX, sizeY;\r
7188   int newSizeX, newSizeY;\r
7189   MINMAXINFO *mmi;\r
7190   WORD wMask;\r
7191 \r
7192   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7193   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7194 \r
7195   switch (message) {\r
7196   case WM_NOTIFY:\r
7197     if (((NMHDR*)lParam)->code == EN_LINK)\r
7198     {\r
7199       ENLINK *pLink = (ENLINK*)lParam;\r
7200       if (pLink->msg == WM_LBUTTONUP)\r
7201       {\r
7202         TEXTRANGE tr;\r
7203 \r
7204         tr.chrg = pLink->chrg;\r
7205         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7206         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7207         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7208         free(tr.lpstrText);\r
7209       }\r
7210     }\r
7211     break;\r
7212   case WM_INITDIALOG: /* message: initialize dialog box */\r
7213     hwndConsole = hDlg;\r
7214     SetFocus(hInput);\r
7215     consoleTextWindowProc = (WNDPROC)\r
7216       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7217     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7218     consoleInputWindowProc = (WNDPROC)\r
7219       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7220     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7221     Colorize(ColorNormal, TRUE);\r
7222     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7223     ChangedConsoleFont();\r
7224     GetClientRect(hDlg, &rect);\r
7225     sizeX = rect.right;\r
7226     sizeY = rect.bottom;\r
7227     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7228         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7229       WINDOWPLACEMENT wp;\r
7230       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7231       wp.length = sizeof(WINDOWPLACEMENT);\r
7232       wp.flags = 0;\r
7233       wp.showCmd = SW_SHOW;\r
7234       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7235       wp.rcNormalPosition.left = wpConsole.x;\r
7236       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7237       wp.rcNormalPosition.top = wpConsole.y;\r
7238       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7239       SetWindowPlacement(hDlg, &wp);\r
7240     }\r
7241 \r
7242    // [HGM] Chessknight's change 2004-07-13\r
7243    else { /* Determine Defaults */\r
7244        WINDOWPLACEMENT wp;\r
7245        wpConsole.x = wpMain.width + 1;\r
7246        wpConsole.y = wpMain.y;\r
7247        wpConsole.width = screenWidth -  wpMain.width;\r
7248        wpConsole.height = wpMain.height;\r
7249        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7250        wp.length = sizeof(WINDOWPLACEMENT);\r
7251        wp.flags = 0;\r
7252        wp.showCmd = SW_SHOW;\r
7253        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7254        wp.rcNormalPosition.left = wpConsole.x;\r
7255        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7256        wp.rcNormalPosition.top = wpConsole.y;\r
7257        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7258        SetWindowPlacement(hDlg, &wp);\r
7259     }\r
7260 \r
7261    // Allow hText to highlight URLs and send notifications on them\r
7262    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7263    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7264    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7265    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7266 \r
7267     return FALSE;\r
7268 \r
7269   case WM_SETFOCUS:\r
7270     SetFocus(hInput);\r
7271     return 0;\r
7272 \r
7273   case WM_CLOSE:\r
7274     ExitEvent(0);\r
7275     /* not reached */\r
7276     break;\r
7277 \r
7278   case WM_SIZE:\r
7279     if (IsIconic(hDlg)) break;\r
7280     newSizeX = LOWORD(lParam);\r
7281     newSizeY = HIWORD(lParam);\r
7282     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7283       RECT rectText, rectInput;\r
7284       POINT pt;\r
7285       int newTextHeight, newTextWidth;\r
7286       GetWindowRect(hText, &rectText);\r
7287       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7288       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7289       if (newTextHeight < 0) {\r
7290         newSizeY += -newTextHeight;\r
7291         newTextHeight = 0;\r
7292       }\r
7293       SetWindowPos(hText, NULL, 0, 0,\r
7294         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7295       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7296       pt.x = rectInput.left;\r
7297       pt.y = rectInput.top + newSizeY - sizeY;\r
7298       ScreenToClient(hDlg, &pt);\r
7299       SetWindowPos(hInput, NULL, \r
7300         pt.x, pt.y, /* needs client coords */   \r
7301         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7302         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7303     }\r
7304     sizeX = newSizeX;\r
7305     sizeY = newSizeY;\r
7306     break;\r
7307 \r
7308   case WM_GETMINMAXINFO:\r
7309     /* Prevent resizing window too small */\r
7310     mmi = (MINMAXINFO *) lParam;\r
7311     mmi->ptMinTrackSize.x = 100;\r
7312     mmi->ptMinTrackSize.y = 100;\r
7313     break;\r
7314 \r
7315   /* [AS] Snapping */\r
7316   case WM_ENTERSIZEMOVE:\r
7317     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7318 \r
7319   case WM_SIZING:\r
7320     return OnSizing( &sd, hDlg, wParam, lParam );\r
7321 \r
7322   case WM_MOVING:\r
7323     return OnMoving( &sd, hDlg, wParam, lParam );\r
7324 \r
7325   case WM_EXITSIZEMOVE:\r
7326         UpdateICSWidth(hText);\r
7327     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7328   }\r
7329 \r
7330   return DefWindowProc(hDlg, message, wParam, lParam);\r
7331 }\r
7332 \r
7333 \r
7334 VOID\r
7335 ConsoleCreate()\r
7336 {\r
7337   HWND hCons;\r
7338   if (hwndConsole) return;\r
7339   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7340   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7341 }\r
7342 \r
7343 \r
7344 VOID\r
7345 ConsoleOutput(char* data, int length, int forceVisible)\r
7346 {\r
7347   HWND hText;\r
7348   int trim, exlen;\r
7349   char *p, *q;\r
7350   char buf[CO_MAX+1];\r
7351   POINT pEnd;\r
7352   RECT rect;\r
7353   static int delayLF = 0;\r
7354   CHARRANGE savesel, sel;\r
7355 \r
7356   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7357   p = data;\r
7358   q = buf;\r
7359   if (delayLF) {\r
7360     *q++ = '\r';\r
7361     *q++ = '\n';\r
7362     delayLF = 0;\r
7363   }\r
7364   while (length--) {\r
7365     if (*p == '\n') {\r
7366       if (*++p) {\r
7367         *q++ = '\r';\r
7368         *q++ = '\n';\r
7369       } else {\r
7370         delayLF = 1;\r
7371       }\r
7372     } else if (*p == '\007') {\r
7373        MyPlaySound(&sounds[(int)SoundBell]);\r
7374        p++;\r
7375     } else {\r
7376       *q++ = *p++;\r
7377     }\r
7378   }\r
7379   *q = NULLCHAR;\r
7380   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7381   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7382   /* Save current selection */\r
7383   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7384   exlen = GetWindowTextLength(hText);\r
7385   /* Find out whether current end of text is visible */\r
7386   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7387   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7388   /* Trim existing text if it's too long */\r
7389   if (exlen + (q - buf) > CO_MAX) {\r
7390     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7391     sel.cpMin = 0;\r
7392     sel.cpMax = trim;\r
7393     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7394     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7395     exlen -= trim;\r
7396     savesel.cpMin -= trim;\r
7397     savesel.cpMax -= trim;\r
7398     if (exlen < 0) exlen = 0;\r
7399     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7400     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7401   }\r
7402   /* Append the new text */\r
7403   sel.cpMin = exlen;\r
7404   sel.cpMax = exlen;\r
7405   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7406   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7407   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7408   if (forceVisible || exlen == 0 ||\r
7409       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7410        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7411     /* Scroll to make new end of text visible if old end of text\r
7412        was visible or new text is an echo of user typein */\r
7413     sel.cpMin = 9999999;\r
7414     sel.cpMax = 9999999;\r
7415     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7416     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7417     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7418     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7419   }\r
7420   if (savesel.cpMax == exlen || forceVisible) {\r
7421     /* Move insert point to new end of text if it was at the old\r
7422        end of text or if the new text is an echo of user typein */\r
7423     sel.cpMin = 9999999;\r
7424     sel.cpMax = 9999999;\r
7425     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7426   } else {\r
7427     /* Restore previous selection */\r
7428     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7429   }\r
7430   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7431 }\r
7432 \r
7433 /*---------*/\r
7434 \r
7435 \r
7436 void\r
7437 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7438 {\r
7439   char buf[100];\r
7440   char *str;\r
7441   COLORREF oldFg, oldBg;\r
7442   HFONT oldFont;\r
7443   RECT rect;\r
7444 \r
7445   if(copyNumber > 1)
7446     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7447 \r
7448   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7449   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7450   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7451 \r
7452   rect.left = x;\r
7453   rect.right = x + squareSize;\r
7454   rect.top  = y;\r
7455   rect.bottom = y + squareSize;\r
7456   str = buf;\r
7457 \r
7458   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7459                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7460              y, ETO_CLIPPED|ETO_OPAQUE,\r
7461              &rect, str, strlen(str), NULL);\r
7462 \r
7463   (void) SetTextColor(hdc, oldFg);\r
7464   (void) SetBkColor(hdc, oldBg);\r
7465   (void) SelectObject(hdc, oldFont);\r
7466 }\r
7467 \r
7468 void\r
7469 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7470               RECT *rect, char *color, char *flagFell)\r
7471 {\r
7472   char buf[100];\r
7473   char *str;\r
7474   COLORREF oldFg, oldBg;\r
7475   HFONT oldFont;\r
7476 \r
7477   if (appData.clockMode) {\r
7478     if (tinyLayout)\r
7479       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7480     else\r
7481       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7482     str = buf;\r
7483   } else {\r
7484     str = color;\r
7485   }\r
7486 \r
7487   if (highlight) {\r
7488     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7489     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7490   } else {\r
7491     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7492     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7493   }\r
7494   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7495 \r
7496   JAWS_SILENCE\r
7497 \r
7498   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7499              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7500              rect, str, strlen(str), NULL);\r
7501   if(logoHeight > 0 && appData.clockMode) {\r
7502       RECT r;\r
7503       str += strlen(color)+2;\r
7504       r.top = rect->top + logoHeight/2;\r
7505       r.left = rect->left;\r
7506       r.right = rect->right;\r
7507       r.bottom = rect->bottom;\r
7508       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7509                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7510                  &r, str, strlen(str), NULL);\r
7511   }\r
7512   (void) SetTextColor(hdc, oldFg);\r
7513   (void) SetBkColor(hdc, oldBg);\r
7514   (void) SelectObject(hdc, oldFont);\r
7515 }\r
7516 \r
7517 \r
7518 int\r
7519 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7520            OVERLAPPED *ovl)\r
7521 {\r
7522   int ok, err;\r
7523 \r
7524   /* [AS]  */\r
7525   if( count <= 0 ) {\r
7526     if (appData.debugMode) {\r
7527       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7528     }\r
7529 \r
7530     return ERROR_INVALID_USER_BUFFER;\r
7531   }\r
7532 \r
7533   ResetEvent(ovl->hEvent);\r
7534   ovl->Offset = ovl->OffsetHigh = 0;\r
7535   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7536   if (ok) {\r
7537     err = NO_ERROR;\r
7538   } else {\r
7539     err = GetLastError();\r
7540     if (err == ERROR_IO_PENDING) {\r
7541       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7542       if (ok)\r
7543         err = NO_ERROR;\r
7544       else\r
7545         err = GetLastError();\r
7546     }\r
7547   }\r
7548   return err;\r
7549 }\r
7550 \r
7551 int\r
7552 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7553             OVERLAPPED *ovl)\r
7554 {\r
7555   int ok, err;\r
7556 \r
7557   ResetEvent(ovl->hEvent);\r
7558   ovl->Offset = ovl->OffsetHigh = 0;\r
7559   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7560   if (ok) {\r
7561     err = NO_ERROR;\r
7562   } else {\r
7563     err = GetLastError();\r
7564     if (err == ERROR_IO_PENDING) {\r
7565       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7566       if (ok)\r
7567         err = NO_ERROR;\r
7568       else\r
7569         err = GetLastError();\r
7570     }\r
7571   }\r
7572   return err;\r
7573 }\r
7574 \r
7575 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7576 void CheckForInputBufferFull( InputSource * is )\r
7577 {\r
7578     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7579         /* Look for end of line */\r
7580         char * p = is->buf;\r
7581         \r
7582         while( p < is->next && *p != '\n' ) {\r
7583             p++;\r
7584         }\r
7585 \r
7586         if( p >= is->next ) {\r
7587             if (appData.debugMode) {\r
7588                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7589             }\r
7590 \r
7591             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7592             is->count = (DWORD) -1;\r
7593             is->next = is->buf;\r
7594         }\r
7595     }\r
7596 }\r
7597 \r
7598 DWORD\r
7599 InputThread(LPVOID arg)\r
7600 {\r
7601   InputSource *is;\r
7602   OVERLAPPED ovl;\r
7603 \r
7604   is = (InputSource *) arg;\r
7605   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7606   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7607   while (is->hThread != NULL) {\r
7608     is->error = DoReadFile(is->hFile, is->next,\r
7609                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7610                            &is->count, &ovl);\r
7611     if (is->error == NO_ERROR) {\r
7612       is->next += is->count;\r
7613     } else {\r
7614       if (is->error == ERROR_BROKEN_PIPE) {\r
7615         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7616         is->count = 0;\r
7617       } else {\r
7618         is->count = (DWORD) -1;\r
7619         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7620         break; \r
7621       }\r
7622     }\r
7623 \r
7624     CheckForInputBufferFull( is );\r
7625 \r
7626     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7627 \r
7628     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7629 \r
7630     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7631   }\r
7632 \r
7633   CloseHandle(ovl.hEvent);\r
7634   CloseHandle(is->hFile);\r
7635 \r
7636   if (appData.debugMode) {\r
7637     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7638   }\r
7639 \r
7640   return 0;\r
7641 }\r
7642 \r
7643 \r
7644 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7645 DWORD\r
7646 NonOvlInputThread(LPVOID arg)\r
7647 {\r
7648   InputSource *is;\r
7649   char *p, *q;\r
7650   int i;\r
7651   char prev;\r
7652 \r
7653   is = (InputSource *) arg;\r
7654   while (is->hThread != NULL) {\r
7655     is->error = ReadFile(is->hFile, is->next,\r
7656                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7657                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7658     if (is->error == NO_ERROR) {\r
7659       /* Change CRLF to LF */\r
7660       if (is->next > is->buf) {\r
7661         p = is->next - 1;\r
7662         i = is->count + 1;\r
7663       } else {\r
7664         p = is->next;\r
7665         i = is->count;\r
7666       }\r
7667       q = p;\r
7668       prev = NULLCHAR;\r
7669       while (i > 0) {\r
7670         if (prev == '\r' && *p == '\n') {\r
7671           *(q-1) = '\n';\r
7672           is->count--;\r
7673         } else { \r
7674           *q++ = *p;\r
7675         }\r
7676         prev = *p++;\r
7677         i--;\r
7678       }\r
7679       *q = NULLCHAR;\r
7680       is->next = q;\r
7681     } else {\r
7682       if (is->error == ERROR_BROKEN_PIPE) {\r
7683         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7684         is->count = 0; \r
7685       } else {\r
7686         is->count = (DWORD) -1;\r
7687       }\r
7688     }\r
7689 \r
7690     CheckForInputBufferFull( is );\r
7691 \r
7692     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7693 \r
7694     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7695 \r
7696     if (is->count < 0) break;  /* Quit on error */\r
7697   }\r
7698   CloseHandle(is->hFile);\r
7699   return 0;\r
7700 }\r
7701 \r
7702 DWORD\r
7703 SocketInputThread(LPVOID arg)\r
7704 {\r
7705   InputSource *is;\r
7706 \r
7707   is = (InputSource *) arg;\r
7708   while (is->hThread != NULL) {\r
7709     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7710     if ((int)is->count == SOCKET_ERROR) {\r
7711       is->count = (DWORD) -1;\r
7712       is->error = WSAGetLastError();\r
7713     } else {\r
7714       is->error = NO_ERROR;\r
7715       is->next += is->count;\r
7716       if (is->count == 0 && is->second == is) {\r
7717         /* End of file on stderr; quit with no message */\r
7718         break;\r
7719       }\r
7720     }\r
7721     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7722 \r
7723     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7724 \r
7725     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7726   }\r
7727   return 0;\r
7728 }\r
7729 \r
7730 VOID\r
7731 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7732 {\r
7733   InputSource *is;\r
7734 \r
7735   is = (InputSource *) lParam;\r
7736   if (is->lineByLine) {\r
7737     /* Feed in lines one by one */\r
7738     char *p = is->buf;\r
7739     char *q = p;\r
7740     while (q < is->next) {\r
7741       if (*q++ == '\n') {\r
7742         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7743         p = q;\r
7744       }\r
7745     }\r
7746     \r
7747     /* Move any partial line to the start of the buffer */\r
7748     q = is->buf;\r
7749     while (p < is->next) {\r
7750       *q++ = *p++;\r
7751     }\r
7752     is->next = q;\r
7753 \r
7754     if (is->error != NO_ERROR || is->count == 0) {\r
7755       /* Notify backend of the error.  Note: If there was a partial\r
7756          line at the end, it is not flushed through. */\r
7757       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7758     }\r
7759   } else {\r
7760     /* Feed in the whole chunk of input at once */\r
7761     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7762     is->next = is->buf;\r
7763   }\r
7764 }\r
7765 \r
7766 /*---------------------------------------------------------------------------*\\r
7767  *\r
7768  *  Menu enables. Used when setting various modes.\r
7769  *\r
7770 \*---------------------------------------------------------------------------*/\r
7771 \r
7772 typedef struct {\r
7773   int item;\r
7774   int flags;\r
7775 } Enables;\r
7776 \r
7777 VOID\r
7778 GreyRevert(Boolean grey)\r
7779 { // [HGM] vari: for retracting variations in local mode\r
7780   HMENU hmenu = GetMenu(hwndMain);\r
7781   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7782   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7783 }\r
7784 \r
7785 VOID\r
7786 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7787 {\r
7788   while (enab->item > 0) {\r
7789     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7790     enab++;\r
7791   }\r
7792 }\r
7793 \r
7794 Enables gnuEnables[] = {\r
7795   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7808 \r
7809   // Needed to switch from ncp to GNU mode on Engine Load\r
7810   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7811   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7817   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7818   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7819   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7820   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7822   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7823   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7824   { -1, -1 }\r
7825 };\r
7826 \r
7827 Enables icsEnables[] = {\r
7828   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7836   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7847   { -1, -1 }\r
7848 };\r
7849 \r
7850 #if ZIPPY\r
7851 Enables zippyEnables[] = {\r
7852   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7856   { -1, -1 }\r
7857 };\r
7858 #endif\r
7859 \r
7860 Enables ncpEnables[] = {\r
7861   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7870   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7871   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7883   { -1, -1 }\r
7884 };\r
7885 \r
7886 Enables trainingOnEnables[] = {\r
7887   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7896   { -1, -1 }\r
7897 };\r
7898 \r
7899 Enables trainingOffEnables[] = {\r
7900   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7909   { -1, -1 }\r
7910 };\r
7911 \r
7912 /* These modify either ncpEnables or gnuEnables */\r
7913 Enables cmailEnables[] = {\r
7914   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7916   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7917   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7921   { -1, -1 }\r
7922 };\r
7923 \r
7924 Enables machineThinkingEnables[] = {\r
7925   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7938 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7941   { -1, -1 }\r
7942 };\r
7943 \r
7944 Enables userThinkingEnables[] = {\r
7945   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7958 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7960   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7961   { -1, -1 }\r
7962 };\r
7963 \r
7964 /*---------------------------------------------------------------------------*\\r
7965  *\r
7966  *  Front-end interface functions exported by XBoard.\r
7967  *  Functions appear in same order as prototypes in frontend.h.\r
7968  * \r
7969 \*---------------------------------------------------------------------------*/\r
7970 VOID\r
7971 CheckMark(UINT item, int state)\r
7972 {\r
7973     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7974 }\r
7975 \r
7976 VOID\r
7977 ModeHighlight()\r
7978 {\r
7979   static UINT prevChecked = 0;\r
7980   static int prevPausing = 0;\r
7981   UINT nowChecked;\r
7982 \r
7983   if (pausing != prevPausing) {\r
7984     prevPausing = pausing;\r
7985     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7986                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7987     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7988   }\r
7989 \r
7990   switch (gameMode) {\r
7991   case BeginningOfGame:\r
7992     if (appData.icsActive)\r
7993       nowChecked = IDM_IcsClient;\r
7994     else if (appData.noChessProgram)\r
7995       nowChecked = IDM_EditGame;\r
7996     else\r
7997       nowChecked = IDM_MachineBlack;\r
7998     break;\r
7999   case MachinePlaysBlack:\r
8000     nowChecked = IDM_MachineBlack;\r
8001     break;\r
8002   case MachinePlaysWhite:\r
8003     nowChecked = IDM_MachineWhite;\r
8004     break;\r
8005   case TwoMachinesPlay:\r
8006     nowChecked = IDM_TwoMachines;\r
8007     break;\r
8008   case AnalyzeMode:\r
8009     nowChecked = IDM_AnalysisMode;\r
8010     break;\r
8011   case AnalyzeFile:\r
8012     nowChecked = IDM_AnalyzeFile;\r
8013     break;\r
8014   case EditGame:\r
8015     nowChecked = IDM_EditGame;\r
8016     break;\r
8017   case PlayFromGameFile:\r
8018     nowChecked = IDM_LoadGame;\r
8019     break;\r
8020   case EditPosition:\r
8021     nowChecked = IDM_EditPosition;\r
8022     break;\r
8023   case Training:\r
8024     nowChecked = IDM_Training;\r
8025     break;\r
8026   case IcsPlayingWhite:\r
8027   case IcsPlayingBlack:\r
8028   case IcsObserving:\r
8029   case IcsIdle:\r
8030     nowChecked = IDM_IcsClient;\r
8031     break;\r
8032   default:\r
8033   case EndOfGame:\r
8034     nowChecked = 0;\r
8035     break;\r
8036   }\r
8037   CheckMark(prevChecked, MF_UNCHECKED);\r
8038   CheckMark(nowChecked, MF_CHECKED);\r
8039   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8040 \r
8041   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8042     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8043                           MF_BYCOMMAND|MF_ENABLED);\r
8044   } else {\r
8045     (void) EnableMenuItem(GetMenu(hwndMain), \r
8046                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8047   }\r
8048 \r
8049   prevChecked = nowChecked;\r
8050 \r
8051   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8052   if (appData.icsActive) {\r
8053        if (appData.icsEngineAnalyze) {\r
8054                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8055        } else {\r
8056                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8057        }\r
8058   }\r
8059   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8060 }\r
8061 \r
8062 VOID\r
8063 SetICSMode()\r
8064 {\r
8065   HMENU hmenu = GetMenu(hwndMain);\r
8066   SetMenuEnables(hmenu, icsEnables);\r
8067   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8068     MF_BYCOMMAND|MF_ENABLED);\r
8069 #if ZIPPY\r
8070   if (appData.zippyPlay) {\r
8071     SetMenuEnables(hmenu, zippyEnables);\r
8072     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8073          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8074           MF_BYCOMMAND|MF_ENABLED);\r
8075   }\r
8076 #endif\r
8077 }\r
8078 \r
8079 VOID\r
8080 SetGNUMode()\r
8081 {\r
8082   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8083 }\r
8084 \r
8085 VOID\r
8086 SetNCPMode()\r
8087 {\r
8088   HMENU hmenu = GetMenu(hwndMain);\r
8089   SetMenuEnables(hmenu, ncpEnables);\r
8090     DrawMenuBar(hwndMain);\r
8091 }\r
8092 \r
8093 VOID\r
8094 SetCmailMode()\r
8095 {\r
8096   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8097 }\r
8098 \r
8099 VOID \r
8100 SetTrainingModeOn()\r
8101 {\r
8102   int i;\r
8103   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8104   for (i = 0; i < N_BUTTONS; i++) {\r
8105     if (buttonDesc[i].hwnd != NULL)\r
8106       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8107   }\r
8108   CommentPopDown();\r
8109 }\r
8110 \r
8111 VOID SetTrainingModeOff()\r
8112 {\r
8113   int i;\r
8114   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8115   for (i = 0; i < N_BUTTONS; i++) {\r
8116     if (buttonDesc[i].hwnd != NULL)\r
8117       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8118   }\r
8119 }\r
8120 \r
8121 \r
8122 VOID\r
8123 SetUserThinkingEnables()\r
8124 {\r
8125   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8126 }\r
8127 \r
8128 VOID\r
8129 SetMachineThinkingEnables()\r
8130 {\r
8131   HMENU hMenu = GetMenu(hwndMain);\r
8132   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8133 \r
8134   SetMenuEnables(hMenu, machineThinkingEnables);\r
8135 \r
8136   if (gameMode == MachinePlaysBlack) {\r
8137     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8138   } else if (gameMode == MachinePlaysWhite) {\r
8139     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8140   } else if (gameMode == TwoMachinesPlay) {\r
8141     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8142   }\r
8143 }\r
8144 \r
8145 \r
8146 VOID\r
8147 DisplayTitle(char *str)\r
8148 {\r
8149   char title[MSG_SIZ], *host;\r
8150   if (str[0] != NULLCHAR) {\r
8151     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8152   } else if (appData.icsActive) {\r
8153     if (appData.icsCommPort[0] != NULLCHAR)\r
8154       host = "ICS";\r
8155     else \r
8156       host = appData.icsHost;\r
8157       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8158   } else if (appData.noChessProgram) {\r
8159     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8160   } else {\r
8161     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8162     strcat(title, ": ");\r
8163     strcat(title, first.tidy);\r
8164   }\r
8165   SetWindowText(hwndMain, title);\r
8166 }\r
8167 \r
8168 \r
8169 VOID\r
8170 DisplayMessage(char *str1, char *str2)\r
8171 {\r
8172   HDC hdc;\r
8173   HFONT oldFont;\r
8174   int remain = MESSAGE_TEXT_MAX - 1;\r
8175   int len;\r
8176 \r
8177   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8178   messageText[0] = NULLCHAR;\r
8179   if (*str1) {\r
8180     len = strlen(str1);\r
8181     if (len > remain) len = remain;\r
8182     strncpy(messageText, str1, len);\r
8183     messageText[len] = NULLCHAR;\r
8184     remain -= len;\r
8185   }\r
8186   if (*str2 && remain >= 2) {\r
8187     if (*str1) {\r
8188       strcat(messageText, "  ");\r
8189       remain -= 2;\r
8190     }\r
8191     len = strlen(str2);\r
8192     if (len > remain) len = remain;\r
8193     strncat(messageText, str2, len);\r
8194   }\r
8195   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8196   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8197 \r
8198   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8199 \r
8200   SAYMACHINEMOVE();\r
8201 \r
8202   hdc = GetDC(hwndMain);\r
8203   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8204   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8205              &messageRect, messageText, strlen(messageText), NULL);\r
8206   (void) SelectObject(hdc, oldFont);\r
8207   (void) ReleaseDC(hwndMain, hdc);\r
8208 }\r
8209 \r
8210 VOID\r
8211 DisplayError(char *str, int error)\r
8212 {\r
8213   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8214   int len;\r
8215 \r
8216   if (error == 0) {\r
8217     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8218   } else {\r
8219     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8220                         NULL, error, LANG_NEUTRAL,\r
8221                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8222     if (len > 0) {\r
8223       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8224     } else {\r
8225       ErrorMap *em = errmap;\r
8226       while (em->err != 0 && em->err != error) em++;\r
8227       if (em->err != 0) {\r
8228         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8229       } else {\r
8230         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8231       }\r
8232     }\r
8233   }\r
8234   \r
8235   ErrorPopUp(_("Error"), buf);\r
8236 }\r
8237 \r
8238 \r
8239 VOID\r
8240 DisplayMoveError(char *str)\r
8241 {\r
8242   fromX = fromY = -1;\r
8243   ClearHighlights();\r
8244   DrawPosition(FALSE, NULL);\r
8245   if (appData.popupMoveErrors) {\r
8246     ErrorPopUp(_("Error"), str);\r
8247   } else {\r
8248     DisplayMessage(str, "");\r
8249     moveErrorMessageUp = TRUE;\r
8250   }\r
8251 }\r
8252 \r
8253 VOID\r
8254 DisplayFatalError(char *str, int error, int exitStatus)\r
8255 {\r
8256   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8257   int len;\r
8258   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8259 \r
8260   if (error != 0) {\r
8261     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8262                         NULL, error, LANG_NEUTRAL,\r
8263                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8264     if (len > 0) {\r
8265       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8266     } else {\r
8267       ErrorMap *em = errmap;\r
8268       while (em->err != 0 && em->err != error) em++;\r
8269       if (em->err != 0) {\r
8270         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8271       } else {\r
8272         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8273       }\r
8274     }\r
8275     str = buf;\r
8276   }\r
8277   if (appData.debugMode) {\r
8278     fprintf(debugFP, "%s: %s\n", label, str);\r
8279   }\r
8280   if (appData.popupExitMessage) {\r
8281     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8282                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8283   }\r
8284   ExitEvent(exitStatus);\r
8285 }\r
8286 \r
8287 \r
8288 VOID\r
8289 DisplayInformation(char *str)\r
8290 {\r
8291   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8292 }\r
8293 \r
8294 \r
8295 VOID\r
8296 DisplayNote(char *str)\r
8297 {\r
8298   ErrorPopUp(_("Note"), str);\r
8299 }\r
8300 \r
8301 \r
8302 typedef struct {\r
8303   char *title, *question, *replyPrefix;\r
8304   ProcRef pr;\r
8305 } QuestionParams;\r
8306 \r
8307 LRESULT CALLBACK\r
8308 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8309 {\r
8310   static QuestionParams *qp;\r
8311   char reply[MSG_SIZ];\r
8312   int len, err;\r
8313 \r
8314   switch (message) {\r
8315   case WM_INITDIALOG:\r
8316     qp = (QuestionParams *) lParam;\r
8317     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8318     Translate(hDlg, DLG_Question);\r
8319     SetWindowText(hDlg, qp->title);\r
8320     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8321     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8322     return FALSE;\r
8323 \r
8324   case WM_COMMAND:\r
8325     switch (LOWORD(wParam)) {\r
8326     case IDOK:\r
8327       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8328       if (*reply) strcat(reply, " ");\r
8329       len = strlen(reply);\r
8330       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8331       strcat(reply, "\n");\r
8332       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8333       EndDialog(hDlg, TRUE);\r
8334       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8335       return TRUE;\r
8336     case IDCANCEL:\r
8337       EndDialog(hDlg, FALSE);\r
8338       return TRUE;\r
8339     default:\r
8340       break;\r
8341     }\r
8342     break;\r
8343   }\r
8344   return FALSE;\r
8345 }\r
8346 \r
8347 VOID\r
8348 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8349 {\r
8350     QuestionParams qp;\r
8351     FARPROC lpProc;\r
8352     \r
8353     qp.title = title;\r
8354     qp.question = question;\r
8355     qp.replyPrefix = replyPrefix;\r
8356     qp.pr = pr;\r
8357     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8358     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8359       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8360     FreeProcInstance(lpProc);\r
8361 }\r
8362 \r
8363 /* [AS] Pick FRC position */\r
8364 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8365 {\r
8366     static int * lpIndexFRC;\r
8367     BOOL index_is_ok;\r
8368     char buf[16];\r
8369 \r
8370     switch( message )\r
8371     {\r
8372     case WM_INITDIALOG:\r
8373         lpIndexFRC = (int *) lParam;\r
8374 \r
8375         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8376         Translate(hDlg, DLG_NewGameFRC);\r
8377 \r
8378         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8379         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8380         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8381         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8382 \r
8383         break;\r
8384 \r
8385     case WM_COMMAND:\r
8386         switch( LOWORD(wParam) ) {\r
8387         case IDOK:\r
8388             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8389             EndDialog( hDlg, 0 );\r
8390             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8391             return TRUE;\r
8392         case IDCANCEL:\r
8393             EndDialog( hDlg, 1 );   \r
8394             return TRUE;\r
8395         case IDC_NFG_Edit:\r
8396             if( HIWORD(wParam) == EN_CHANGE ) {\r
8397                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8398 \r
8399                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8400             }\r
8401             return TRUE;\r
8402         case IDC_NFG_Random:\r
8403           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8404             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8405             return TRUE;\r
8406         }\r
8407 \r
8408         break;\r
8409     }\r
8410 \r
8411     return FALSE;\r
8412 }\r
8413 \r
8414 int NewGameFRC()\r
8415 {\r
8416     int result;\r
8417     int index = appData.defaultFrcPosition;\r
8418     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8419 \r
8420     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8421 \r
8422     if( result == 0 ) {\r
8423         appData.defaultFrcPosition = index;\r
8424     }\r
8425 \r
8426     return result;\r
8427 }\r
8428 \r
8429 /* [AS] Game list options. Refactored by HGM */\r
8430 \r
8431 HWND gameListOptionsDialog;\r
8432 \r
8433 // low-level front-end: clear text edit / list widget\r
8434 void\r
8435 GLT_ClearList()\r
8436 {\r
8437     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8438 }\r
8439 \r
8440 // low-level front-end: clear text edit / list widget\r
8441 void\r
8442 GLT_DeSelectList()\r
8443 {\r
8444     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8445 }\r
8446 \r
8447 // low-level front-end: append line to text edit / list widget\r
8448 void\r
8449 GLT_AddToList( char *name )\r
8450 {\r
8451     if( name != 0 ) {\r
8452             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8453     }\r
8454 }\r
8455 \r
8456 // low-level front-end: get line from text edit / list widget\r
8457 Boolean\r
8458 GLT_GetFromList( int index, char *name )\r
8459 {\r
8460     if( name != 0 ) {\r
8461             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8462                 return TRUE;\r
8463     }\r
8464     return FALSE;\r
8465 }\r
8466 \r
8467 void GLT_MoveSelection( HWND hDlg, int delta )\r
8468 {\r
8469     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8470     int idx2 = idx1 + delta;\r
8471     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8472 \r
8473     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8474         char buf[128];\r
8475 \r
8476         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8477         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8478         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8479         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8480     }\r
8481 }\r
8482 \r
8483 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8484 {\r
8485     switch( message )\r
8486     {\r
8487     case WM_INITDIALOG:\r
8488         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8489         \r
8490         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8491         Translate(hDlg, DLG_GameListOptions);\r
8492 \r
8493         /* Initialize list */\r
8494         GLT_TagsToList( lpUserGLT );\r
8495 \r
8496         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8497 \r
8498         break;\r
8499 \r
8500     case WM_COMMAND:\r
8501         switch( LOWORD(wParam) ) {\r
8502         case IDOK:\r
8503             GLT_ParseList();\r
8504             EndDialog( hDlg, 0 );\r
8505             return TRUE;\r
8506         case IDCANCEL:\r
8507             EndDialog( hDlg, 1 );\r
8508             return TRUE;\r
8509 \r
8510         case IDC_GLT_Default:\r
8511             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8512             return TRUE;\r
8513 \r
8514         case IDC_GLT_Restore:\r
8515             GLT_TagsToList( appData.gameListTags );\r
8516             return TRUE;\r
8517 \r
8518         case IDC_GLT_Up:\r
8519             GLT_MoveSelection( hDlg, -1 );\r
8520             return TRUE;\r
8521 \r
8522         case IDC_GLT_Down:\r
8523             GLT_MoveSelection( hDlg, +1 );\r
8524             return TRUE;\r
8525         }\r
8526 \r
8527         break;\r
8528     }\r
8529 \r
8530     return FALSE;\r
8531 }\r
8532 \r
8533 int GameListOptions()\r
8534 {\r
8535     int result;\r
8536     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8537 \r
8538       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8539 \r
8540     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8541 \r
8542     if( result == 0 ) {\r
8543         /* [AS] Memory leak here! */\r
8544         appData.gameListTags = strdup( lpUserGLT ); \r
8545     }\r
8546 \r
8547     return result;\r
8548 }\r
8549 \r
8550 VOID\r
8551 DisplayIcsInteractionTitle(char *str)\r
8552 {\r
8553   char consoleTitle[MSG_SIZ];\r
8554 \r
8555     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8556   SetWindowText(hwndConsole, consoleTitle);\r
8557 }\r
8558 \r
8559 void\r
8560 DrawPosition(int fullRedraw, Board board)\r
8561 {\r
8562   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8563 }\r
8564 \r
8565 void NotifyFrontendLogin()\r
8566 {\r
8567         if (hwndConsole)\r
8568                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8569 }\r
8570 \r
8571 VOID\r
8572 ResetFrontEnd()\r
8573 {\r
8574   fromX = fromY = -1;\r
8575   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8576     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8577     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8578     dragInfo.lastpos = dragInfo.pos;\r
8579     dragInfo.start.x = dragInfo.start.y = -1;\r
8580     dragInfo.from = dragInfo.start;\r
8581     ReleaseCapture();\r
8582     DrawPosition(TRUE, NULL);\r
8583   }\r
8584   TagsPopDown();\r
8585 }\r
8586 \r
8587 \r
8588 VOID\r
8589 CommentPopUp(char *title, char *str)\r
8590 {\r
8591   HWND hwnd = GetActiveWindow();\r
8592   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8593   SAY(str);\r
8594   SetActiveWindow(hwnd);\r
8595 }\r
8596 \r
8597 VOID\r
8598 CommentPopDown(void)\r
8599 {\r
8600   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8601   if (commentDialog) {\r
8602     ShowWindow(commentDialog, SW_HIDE);\r
8603   }\r
8604   commentUp = FALSE;\r
8605 }\r
8606 \r
8607 VOID\r
8608 EditCommentPopUp(int index, char *title, char *str)\r
8609 {\r
8610   EitherCommentPopUp(index, title, str, TRUE);\r
8611 }\r
8612 \r
8613 \r
8614 VOID\r
8615 RingBell()\r
8616 {\r
8617   MyPlaySound(&sounds[(int)SoundMove]);\r
8618 }\r
8619 \r
8620 VOID PlayIcsWinSound()\r
8621 {\r
8622   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8623 }\r
8624 \r
8625 VOID PlayIcsLossSound()\r
8626 {\r
8627   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8628 }\r
8629 \r
8630 VOID PlayIcsDrawSound()\r
8631 {\r
8632   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8633 }\r
8634 \r
8635 VOID PlayIcsUnfinishedSound()\r
8636 {\r
8637   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8638 }\r
8639 \r
8640 VOID\r
8641 PlayAlarmSound()\r
8642 {\r
8643   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8644 }\r
8645 \r
8646 VOID\r
8647 PlayTellSound()\r
8648 {\r
8649   MyPlaySound(&textAttribs[ColorTell].sound);\r
8650 }\r
8651 \r
8652 \r
8653 VOID\r
8654 EchoOn()\r
8655 {\r
8656   HWND hInput;\r
8657   consoleEcho = TRUE;\r
8658   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8659   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8660   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8661 }\r
8662 \r
8663 \r
8664 VOID\r
8665 EchoOff()\r
8666 {\r
8667   CHARFORMAT cf;\r
8668   HWND hInput;\r
8669   consoleEcho = FALSE;\r
8670   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8671   /* This works OK: set text and background both to the same color */\r
8672   cf = consoleCF;\r
8673   cf.crTextColor = COLOR_ECHOOFF;\r
8674   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8675   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8676 }\r
8677 \r
8678 /* No Raw()...? */\r
8679 \r
8680 void Colorize(ColorClass cc, int continuation)\r
8681 {\r
8682   currentColorClass = cc;\r
8683   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8684   consoleCF.crTextColor = textAttribs[cc].color;\r
8685   consoleCF.dwEffects = textAttribs[cc].effects;\r
8686   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8687 }\r
8688 \r
8689 char *\r
8690 UserName()\r
8691 {\r
8692   static char buf[MSG_SIZ];\r
8693   DWORD bufsiz = MSG_SIZ;\r
8694 \r
8695   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8696         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8697   }\r
8698   if (!GetUserName(buf, &bufsiz)) {\r
8699     /*DisplayError("Error getting user name", GetLastError());*/\r
8700     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8701   }\r
8702   return buf;\r
8703 }\r
8704 \r
8705 char *\r
8706 HostName()\r
8707 {\r
8708   static char buf[MSG_SIZ];\r
8709   DWORD bufsiz = MSG_SIZ;\r
8710 \r
8711   if (!GetComputerName(buf, &bufsiz)) {\r
8712     /*DisplayError("Error getting host name", GetLastError());*/\r
8713     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8714   }\r
8715   return buf;\r
8716 }\r
8717 \r
8718 \r
8719 int\r
8720 ClockTimerRunning()\r
8721 {\r
8722   return clockTimerEvent != 0;\r
8723 }\r
8724 \r
8725 int\r
8726 StopClockTimer()\r
8727 {\r
8728   if (clockTimerEvent == 0) return FALSE;\r
8729   KillTimer(hwndMain, clockTimerEvent);\r
8730   clockTimerEvent = 0;\r
8731   return TRUE;\r
8732 }\r
8733 \r
8734 void\r
8735 StartClockTimer(long millisec)\r
8736 {\r
8737   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8738                              (UINT) millisec, NULL);\r
8739 }\r
8740 \r
8741 void\r
8742 DisplayWhiteClock(long timeRemaining, int highlight)\r
8743 {\r
8744   HDC hdc;\r
8745   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8746 \r
8747   if(appData.noGUI) return;\r
8748   hdc = GetDC(hwndMain);\r
8749   if (!IsIconic(hwndMain)) {\r
8750     DisplayAClock(hdc, timeRemaining, highlight, \r
8751                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8752   }\r
8753   if (highlight && iconCurrent == iconBlack) {\r
8754     iconCurrent = iconWhite;\r
8755     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8756     if (IsIconic(hwndMain)) {\r
8757       DrawIcon(hdc, 2, 2, iconCurrent);\r
8758     }\r
8759   }\r
8760   (void) ReleaseDC(hwndMain, hdc);\r
8761   if (hwndConsole)\r
8762     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8763 }\r
8764 \r
8765 void\r
8766 DisplayBlackClock(long timeRemaining, int highlight)\r
8767 {\r
8768   HDC hdc;\r
8769   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8770 \r
8771   if(appData.noGUI) return;\r
8772   hdc = GetDC(hwndMain);\r
8773   if (!IsIconic(hwndMain)) {\r
8774     DisplayAClock(hdc, timeRemaining, highlight, \r
8775                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8776   }\r
8777   if (highlight && iconCurrent == iconWhite) {\r
8778     iconCurrent = iconBlack;\r
8779     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8780     if (IsIconic(hwndMain)) {\r
8781       DrawIcon(hdc, 2, 2, iconCurrent);\r
8782     }\r
8783   }\r
8784   (void) ReleaseDC(hwndMain, hdc);\r
8785   if (hwndConsole)\r
8786     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8787 }\r
8788 \r
8789 \r
8790 int\r
8791 LoadGameTimerRunning()\r
8792 {\r
8793   return loadGameTimerEvent != 0;\r
8794 }\r
8795 \r
8796 int\r
8797 StopLoadGameTimer()\r
8798 {\r
8799   if (loadGameTimerEvent == 0) return FALSE;\r
8800   KillTimer(hwndMain, loadGameTimerEvent);\r
8801   loadGameTimerEvent = 0;\r
8802   return TRUE;\r
8803 }\r
8804 \r
8805 void\r
8806 StartLoadGameTimer(long millisec)\r
8807 {\r
8808   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8809                                 (UINT) millisec, NULL);\r
8810 }\r
8811 \r
8812 void\r
8813 AutoSaveGame()\r
8814 {\r
8815   char *defName;\r
8816   FILE *f;\r
8817   char fileTitle[MSG_SIZ];\r
8818 \r
8819   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8820   f = OpenFileDialog(hwndMain, "a", defName,\r
8821                      appData.oldSaveStyle ? "gam" : "pgn",\r
8822                      GAME_FILT, \r
8823                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8824   if (f != NULL) {\r
8825     SaveGame(f, 0, "");\r
8826     fclose(f);\r
8827   }\r
8828 }\r
8829 \r
8830 \r
8831 void\r
8832 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8833 {\r
8834   if (delayedTimerEvent != 0) {\r
8835     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8836       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8837     }\r
8838     KillTimer(hwndMain, delayedTimerEvent);\r
8839     delayedTimerEvent = 0;\r
8840     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8841     delayedTimerCallback();\r
8842   }\r
8843   delayedTimerCallback = cb;\r
8844   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8845                                 (UINT) millisec, NULL);\r
8846 }\r
8847 \r
8848 DelayedEventCallback\r
8849 GetDelayedEvent()\r
8850 {\r
8851   if (delayedTimerEvent) {\r
8852     return delayedTimerCallback;\r
8853   } else {\r
8854     return NULL;\r
8855   }\r
8856 }\r
8857 \r
8858 void\r
8859 CancelDelayedEvent()\r
8860 {\r
8861   if (delayedTimerEvent) {\r
8862     KillTimer(hwndMain, delayedTimerEvent);\r
8863     delayedTimerEvent = 0;\r
8864   }\r
8865 }\r
8866 \r
8867 DWORD GetWin32Priority(int nice)\r
8868 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8869 /*\r
8870 REALTIME_PRIORITY_CLASS     0x00000100\r
8871 HIGH_PRIORITY_CLASS         0x00000080\r
8872 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8873 NORMAL_PRIORITY_CLASS       0x00000020\r
8874 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8875 IDLE_PRIORITY_CLASS         0x00000040\r
8876 */\r
8877         if (nice < -15) return 0x00000080;\r
8878         if (nice < 0)   return 0x00008000;\r
8879         if (nice == 0)  return 0x00000020;\r
8880         if (nice < 15)  return 0x00004000;\r
8881         return 0x00000040;\r
8882 }\r
8883 \r
8884 void RunCommand(char *cmdLine)\r
8885 {\r
8886   /* Now create the child process. */\r
8887   STARTUPINFO siStartInfo;\r
8888   PROCESS_INFORMATION piProcInfo;\r
8889 \r
8890   siStartInfo.cb = sizeof(STARTUPINFO);\r
8891   siStartInfo.lpReserved = NULL;\r
8892   siStartInfo.lpDesktop = NULL;\r
8893   siStartInfo.lpTitle = NULL;\r
8894   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8895   siStartInfo.cbReserved2 = 0;\r
8896   siStartInfo.lpReserved2 = NULL;\r
8897   siStartInfo.hStdInput = NULL;\r
8898   siStartInfo.hStdOutput = NULL;\r
8899   siStartInfo.hStdError = NULL;\r
8900 \r
8901   CreateProcess(NULL,\r
8902                 cmdLine,           /* command line */\r
8903                 NULL,      /* process security attributes */\r
8904                 NULL,      /* primary thread security attrs */\r
8905                 TRUE,      /* handles are inherited */\r
8906                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8907                 NULL,      /* use parent's environment */\r
8908                 NULL,\r
8909                 &siStartInfo, /* STARTUPINFO pointer */\r
8910                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8911 \r
8912   CloseHandle(piProcInfo.hThread);\r
8913 }\r
8914 \r
8915 /* Start a child process running the given program.\r
8916    The process's standard output can be read from "from", and its\r
8917    standard input can be written to "to".\r
8918    Exit with fatal error if anything goes wrong.\r
8919    Returns an opaque pointer that can be used to destroy the process\r
8920    later.\r
8921 */\r
8922 int\r
8923 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8924 {\r
8925 #define BUFSIZE 4096\r
8926 \r
8927   HANDLE hChildStdinRd, hChildStdinWr,\r
8928     hChildStdoutRd, hChildStdoutWr;\r
8929   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8930   SECURITY_ATTRIBUTES saAttr;\r
8931   BOOL fSuccess;\r
8932   PROCESS_INFORMATION piProcInfo;\r
8933   STARTUPINFO siStartInfo;\r
8934   ChildProc *cp;\r
8935   char buf[MSG_SIZ];\r
8936   DWORD err;\r
8937 \r
8938   if (appData.debugMode) {\r
8939     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8940   }\r
8941 \r
8942   *pr = NoProc;\r
8943 \r
8944   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8945   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8946   saAttr.bInheritHandle = TRUE;\r
8947   saAttr.lpSecurityDescriptor = NULL;\r
8948 \r
8949   /*\r
8950    * The steps for redirecting child's STDOUT:\r
8951    *     1. Create anonymous pipe to be STDOUT for child.\r
8952    *     2. Create a noninheritable duplicate of read handle,\r
8953    *         and close the inheritable read handle.\r
8954    */\r
8955 \r
8956   /* Create a pipe for the child's STDOUT. */\r
8957   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8958     return GetLastError();\r
8959   }\r
8960 \r
8961   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8962   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8963                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8964                              FALSE,     /* not inherited */\r
8965                              DUPLICATE_SAME_ACCESS);\r
8966   if (! fSuccess) {\r
8967     return GetLastError();\r
8968   }\r
8969   CloseHandle(hChildStdoutRd);\r
8970 \r
8971   /*\r
8972    * The steps for redirecting child's STDIN:\r
8973    *     1. Create anonymous pipe to be STDIN for child.\r
8974    *     2. Create a noninheritable duplicate of write handle,\r
8975    *         and close the inheritable write handle.\r
8976    */\r
8977 \r
8978   /* Create a pipe for the child's STDIN. */\r
8979   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8980     return GetLastError();\r
8981   }\r
8982 \r
8983   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8984   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8985                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8986                              FALSE,     /* not inherited */\r
8987                              DUPLICATE_SAME_ACCESS);\r
8988   if (! fSuccess) {\r
8989     return GetLastError();\r
8990   }\r
8991   CloseHandle(hChildStdinWr);\r
8992 \r
8993   /* Arrange to (1) look in dir for the child .exe file, and\r
8994    * (2) have dir be the child's working directory.  Interpret\r
8995    * dir relative to the directory WinBoard loaded from. */\r
8996   GetCurrentDirectory(MSG_SIZ, buf);\r
8997   SetCurrentDirectory(installDir);\r
8998   SetCurrentDirectory(dir);\r
8999 \r
9000   /* Now create the child process. */\r
9001 \r
9002   siStartInfo.cb = sizeof(STARTUPINFO);\r
9003   siStartInfo.lpReserved = NULL;\r
9004   siStartInfo.lpDesktop = NULL;\r
9005   siStartInfo.lpTitle = NULL;\r
9006   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9007   siStartInfo.cbReserved2 = 0;\r
9008   siStartInfo.lpReserved2 = NULL;\r
9009   siStartInfo.hStdInput = hChildStdinRd;\r
9010   siStartInfo.hStdOutput = hChildStdoutWr;\r
9011   siStartInfo.hStdError = hChildStdoutWr;\r
9012 \r
9013   fSuccess = CreateProcess(NULL,\r
9014                            cmdLine,        /* command line */\r
9015                            NULL,           /* process security attributes */\r
9016                            NULL,           /* primary thread security attrs */\r
9017                            TRUE,           /* handles are inherited */\r
9018                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9019                            NULL,           /* use parent's environment */\r
9020                            NULL,\r
9021                            &siStartInfo, /* STARTUPINFO pointer */\r
9022                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9023 \r
9024   err = GetLastError();\r
9025   SetCurrentDirectory(buf); /* return to prev directory */\r
9026   if (! fSuccess) {\r
9027     return err;\r
9028   }\r
9029 \r
9030   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9031     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9032     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9033   }\r
9034 \r
9035   /* Close the handles we don't need in the parent */\r
9036   CloseHandle(piProcInfo.hThread);\r
9037   CloseHandle(hChildStdinRd);\r
9038   CloseHandle(hChildStdoutWr);\r
9039 \r
9040   /* Prepare return value */\r
9041   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9042   cp->kind = CPReal;\r
9043   cp->hProcess = piProcInfo.hProcess;\r
9044   cp->pid = piProcInfo.dwProcessId;\r
9045   cp->hFrom = hChildStdoutRdDup;\r
9046   cp->hTo = hChildStdinWrDup;\r
9047 \r
9048   *pr = (void *) cp;\r
9049 \r
9050   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9051      2000 where engines sometimes don't see the initial command(s)\r
9052      from WinBoard and hang.  I don't understand how that can happen,\r
9053      but the Sleep is harmless, so I've put it in.  Others have also\r
9054      reported what may be the same problem, so hopefully this will fix\r
9055      it for them too.  */\r
9056   Sleep(500);\r
9057 \r
9058   return NO_ERROR;\r
9059 }\r
9060 \r
9061 \r
9062 void\r
9063 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9064 {\r
9065   ChildProc *cp; int result;\r
9066 \r
9067   cp = (ChildProc *) pr;\r
9068   if (cp == NULL) return;\r
9069 \r
9070   switch (cp->kind) {\r
9071   case CPReal:\r
9072     /* TerminateProcess is considered harmful, so... */\r
9073     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9074     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9075     /* The following doesn't work because the chess program\r
9076        doesn't "have the same console" as WinBoard.  Maybe\r
9077        we could arrange for this even though neither WinBoard\r
9078        nor the chess program uses a console for stdio? */\r
9079     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9080 \r
9081     /* [AS] Special termination modes for misbehaving programs... */\r
9082     if( signal == 9 ) { \r
9083         result = TerminateProcess( cp->hProcess, 0 );\r
9084 \r
9085         if ( appData.debugMode) {\r
9086             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9087         }\r
9088     }\r
9089     else if( signal == 10 ) {\r
9090         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9091 \r
9092         if( dw != WAIT_OBJECT_0 ) {\r
9093             result = TerminateProcess( cp->hProcess, 0 );\r
9094 \r
9095             if ( appData.debugMode) {\r
9096                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9097             }\r
9098 \r
9099         }\r
9100     }\r
9101 \r
9102     CloseHandle(cp->hProcess);\r
9103     break;\r
9104 \r
9105   case CPComm:\r
9106     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9107     break;\r
9108 \r
9109   case CPSock:\r
9110     closesocket(cp->sock);\r
9111     WSACleanup();\r
9112     break;\r
9113 \r
9114   case CPRcmd:\r
9115     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9116     closesocket(cp->sock);\r
9117     closesocket(cp->sock2);\r
9118     WSACleanup();\r
9119     break;\r
9120   }\r
9121   free(cp);\r
9122 }\r
9123 \r
9124 void\r
9125 InterruptChildProcess(ProcRef pr)\r
9126 {\r
9127   ChildProc *cp;\r
9128 \r
9129   cp = (ChildProc *) pr;\r
9130   if (cp == NULL) return;\r
9131   switch (cp->kind) {\r
9132   case CPReal:\r
9133     /* The following doesn't work because the chess program\r
9134        doesn't "have the same console" as WinBoard.  Maybe\r
9135        we could arrange for this even though neither WinBoard\r
9136        nor the chess program uses a console for stdio */\r
9137     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9138     break;\r
9139 \r
9140   case CPComm:\r
9141   case CPSock:\r
9142     /* Can't interrupt */\r
9143     break;\r
9144 \r
9145   case CPRcmd:\r
9146     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9147     break;\r
9148   }\r
9149 }\r
9150 \r
9151 \r
9152 int\r
9153 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9154 {\r
9155   char cmdLine[MSG_SIZ];\r
9156 \r
9157   if (port[0] == NULLCHAR) {\r
9158     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9159   } else {\r
9160     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9161   }\r
9162   return StartChildProcess(cmdLine, "", pr);\r
9163 }\r
9164 \r
9165 \r
9166 /* Code to open TCP sockets */\r
9167 \r
9168 int\r
9169 OpenTCP(char *host, char *port, ProcRef *pr)\r
9170 {\r
9171   ChildProc *cp;\r
9172   int err;\r
9173   SOCKET s;\r
9174   struct sockaddr_in sa, mysa;\r
9175   struct hostent FAR *hp;\r
9176   unsigned short uport;\r
9177   WORD wVersionRequested;\r
9178   WSADATA wsaData;\r
9179 \r
9180   /* Initialize socket DLL */\r
9181   wVersionRequested = MAKEWORD(1, 1);\r
9182   err = WSAStartup(wVersionRequested, &wsaData);\r
9183   if (err != 0) return err;\r
9184 \r
9185   /* Make socket */\r
9186   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9187     err = WSAGetLastError();\r
9188     WSACleanup();\r
9189     return err;\r
9190   }\r
9191 \r
9192   /* Bind local address using (mostly) don't-care values.\r
9193    */\r
9194   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9195   mysa.sin_family = AF_INET;\r
9196   mysa.sin_addr.s_addr = INADDR_ANY;\r
9197   uport = (unsigned short) 0;\r
9198   mysa.sin_port = htons(uport);\r
9199   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9200       == SOCKET_ERROR) {\r
9201     err = WSAGetLastError();\r
9202     WSACleanup();\r
9203     return err;\r
9204   }\r
9205 \r
9206   /* Resolve remote host name */\r
9207   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9208   if (!(hp = gethostbyname(host))) {\r
9209     unsigned int b0, b1, b2, b3;\r
9210 \r
9211     err = WSAGetLastError();\r
9212 \r
9213     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9214       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9215       hp->h_addrtype = AF_INET;\r
9216       hp->h_length = 4;\r
9217       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9218       hp->h_addr_list[0] = (char *) malloc(4);\r
9219       hp->h_addr_list[0][0] = (char) b0;\r
9220       hp->h_addr_list[0][1] = (char) b1;\r
9221       hp->h_addr_list[0][2] = (char) b2;\r
9222       hp->h_addr_list[0][3] = (char) b3;\r
9223     } else {\r
9224       WSACleanup();\r
9225       return err;\r
9226     }\r
9227   }\r
9228   sa.sin_family = hp->h_addrtype;\r
9229   uport = (unsigned short) atoi(port);\r
9230   sa.sin_port = htons(uport);\r
9231   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9232 \r
9233   /* Make connection */\r
9234   if (connect(s, (struct sockaddr *) &sa,\r
9235               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9236     err = WSAGetLastError();\r
9237     WSACleanup();\r
9238     return err;\r
9239   }\r
9240 \r
9241   /* Prepare return value */\r
9242   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9243   cp->kind = CPSock;\r
9244   cp->sock = s;\r
9245   *pr = (ProcRef *) cp;\r
9246 \r
9247   return NO_ERROR;\r
9248 }\r
9249 \r
9250 int\r
9251 OpenCommPort(char *name, ProcRef *pr)\r
9252 {\r
9253   HANDLE h;\r
9254   COMMTIMEOUTS ct;\r
9255   ChildProc *cp;\r
9256   char fullname[MSG_SIZ];\r
9257 \r
9258   if (*name != '\\')\r
9259     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9260   else\r
9261     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9262 \r
9263   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9264                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9265   if (h == (HANDLE) -1) {\r
9266     return GetLastError();\r
9267   }\r
9268   hCommPort = h;\r
9269 \r
9270   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9271 \r
9272   /* Accumulate characters until a 100ms pause, then parse */\r
9273   ct.ReadIntervalTimeout = 100;\r
9274   ct.ReadTotalTimeoutMultiplier = 0;\r
9275   ct.ReadTotalTimeoutConstant = 0;\r
9276   ct.WriteTotalTimeoutMultiplier = 0;\r
9277   ct.WriteTotalTimeoutConstant = 0;\r
9278   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9279 \r
9280   /* Prepare return value */\r
9281   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9282   cp->kind = CPComm;\r
9283   cp->hFrom = h;\r
9284   cp->hTo = h;\r
9285   *pr = (ProcRef *) cp;\r
9286 \r
9287   return NO_ERROR;\r
9288 }\r
9289 \r
9290 int\r
9291 OpenLoopback(ProcRef *pr)\r
9292 {\r
9293   DisplayFatalError(_("Not implemented"), 0, 1);\r
9294   return NO_ERROR;\r
9295 }\r
9296 \r
9297 \r
9298 int\r
9299 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9300 {\r
9301   ChildProc *cp;\r
9302   int err;\r
9303   SOCKET s, s2, s3;\r
9304   struct sockaddr_in sa, mysa;\r
9305   struct hostent FAR *hp;\r
9306   unsigned short uport;\r
9307   WORD wVersionRequested;\r
9308   WSADATA wsaData;\r
9309   int fromPort;\r
9310   char stderrPortStr[MSG_SIZ];\r
9311 \r
9312   /* Initialize socket DLL */\r
9313   wVersionRequested = MAKEWORD(1, 1);\r
9314   err = WSAStartup(wVersionRequested, &wsaData);\r
9315   if (err != 0) return err;\r
9316 \r
9317   /* Resolve remote host name */\r
9318   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9319   if (!(hp = gethostbyname(host))) {\r
9320     unsigned int b0, b1, b2, b3;\r
9321 \r
9322     err = WSAGetLastError();\r
9323 \r
9324     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9325       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9326       hp->h_addrtype = AF_INET;\r
9327       hp->h_length = 4;\r
9328       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9329       hp->h_addr_list[0] = (char *) malloc(4);\r
9330       hp->h_addr_list[0][0] = (char) b0;\r
9331       hp->h_addr_list[0][1] = (char) b1;\r
9332       hp->h_addr_list[0][2] = (char) b2;\r
9333       hp->h_addr_list[0][3] = (char) b3;\r
9334     } else {\r
9335       WSACleanup();\r
9336       return err;\r
9337     }\r
9338   }\r
9339   sa.sin_family = hp->h_addrtype;\r
9340   uport = (unsigned short) 514;\r
9341   sa.sin_port = htons(uport);\r
9342   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9343 \r
9344   /* Bind local socket to unused "privileged" port address\r
9345    */\r
9346   s = INVALID_SOCKET;\r
9347   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9348   mysa.sin_family = AF_INET;\r
9349   mysa.sin_addr.s_addr = INADDR_ANY;\r
9350   for (fromPort = 1023;; fromPort--) {\r
9351     if (fromPort < 0) {\r
9352       WSACleanup();\r
9353       return WSAEADDRINUSE;\r
9354     }\r
9355     if (s == INVALID_SOCKET) {\r
9356       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9357         err = WSAGetLastError();\r
9358         WSACleanup();\r
9359         return err;\r
9360       }\r
9361     }\r
9362     uport = (unsigned short) fromPort;\r
9363     mysa.sin_port = htons(uport);\r
9364     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9365         == SOCKET_ERROR) {\r
9366       err = WSAGetLastError();\r
9367       if (err == WSAEADDRINUSE) continue;\r
9368       WSACleanup();\r
9369       return err;\r
9370     }\r
9371     if (connect(s, (struct sockaddr *) &sa,\r
9372       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9373       err = WSAGetLastError();\r
9374       if (err == WSAEADDRINUSE) {\r
9375         closesocket(s);\r
9376         s = -1;\r
9377         continue;\r
9378       }\r
9379       WSACleanup();\r
9380       return err;\r
9381     }\r
9382     break;\r
9383   }\r
9384 \r
9385   /* Bind stderr local socket to unused "privileged" port address\r
9386    */\r
9387   s2 = 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 == prevStderrPort) continue; // don't reuse port\r
9393     if (fromPort < 0) {\r
9394       (void) closesocket(s);\r
9395       WSACleanup();\r
9396       return WSAEADDRINUSE;\r
9397     }\r
9398     if (s2 == INVALID_SOCKET) {\r
9399       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9400         err = WSAGetLastError();\r
9401         closesocket(s);\r
9402         WSACleanup();\r
9403         return err;\r
9404       }\r
9405     }\r
9406     uport = (unsigned short) fromPort;\r
9407     mysa.sin_port = htons(uport);\r
9408     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9409         == SOCKET_ERROR) {\r
9410       err = WSAGetLastError();\r
9411       if (err == WSAEADDRINUSE) continue;\r
9412       (void) closesocket(s);\r
9413       WSACleanup();\r
9414       return err;\r
9415     }\r
9416     if (listen(s2, 1) == SOCKET_ERROR) {\r
9417       err = WSAGetLastError();\r
9418       if (err == WSAEADDRINUSE) {\r
9419         closesocket(s2);\r
9420         s2 = INVALID_SOCKET;\r
9421         continue;\r
9422       }\r
9423       (void) closesocket(s);\r
9424       (void) closesocket(s2);\r
9425       WSACleanup();\r
9426       return err;\r
9427     }\r
9428     break;\r
9429   }\r
9430   prevStderrPort = fromPort; // remember port used\r
9431   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9432 \r
9433   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9434     err = WSAGetLastError();\r
9435     (void) closesocket(s);\r
9436     (void) closesocket(s2);\r
9437     WSACleanup();\r
9438     return err;\r
9439   }\r
9440 \r
9441   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9442     err = WSAGetLastError();\r
9443     (void) closesocket(s);\r
9444     (void) closesocket(s2);\r
9445     WSACleanup();\r
9446     return err;\r
9447   }\r
9448   if (*user == NULLCHAR) user = UserName();\r
9449   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9450     err = WSAGetLastError();\r
9451     (void) closesocket(s);\r
9452     (void) closesocket(s2);\r
9453     WSACleanup();\r
9454     return err;\r
9455   }\r
9456   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9457     err = WSAGetLastError();\r
9458     (void) closesocket(s);\r
9459     (void) closesocket(s2);\r
9460     WSACleanup();\r
9461     return err;\r
9462   }\r
9463 \r
9464   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9465     err = WSAGetLastError();\r
9466     (void) closesocket(s);\r
9467     (void) closesocket(s2);\r
9468     WSACleanup();\r
9469     return err;\r
9470   }\r
9471   (void) closesocket(s2);  /* Stop listening */\r
9472 \r
9473   /* Prepare return value */\r
9474   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9475   cp->kind = CPRcmd;\r
9476   cp->sock = s;\r
9477   cp->sock2 = s3;\r
9478   *pr = (ProcRef *) cp;\r
9479 \r
9480   return NO_ERROR;\r
9481 }\r
9482 \r
9483 \r
9484 InputSourceRef\r
9485 AddInputSource(ProcRef pr, int lineByLine,\r
9486                InputCallback func, VOIDSTAR closure)\r
9487 {\r
9488   InputSource *is, *is2 = NULL;\r
9489   ChildProc *cp = (ChildProc *) pr;\r
9490 \r
9491   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9492   is->lineByLine = lineByLine;\r
9493   is->func = func;\r
9494   is->closure = closure;\r
9495   is->second = NULL;\r
9496   is->next = is->buf;\r
9497   if (pr == NoProc) {\r
9498     is->kind = CPReal;\r
9499     consoleInputSource = is;\r
9500   } else {\r
9501     is->kind = cp->kind;\r
9502     /* \r
9503         [AS] Try to avoid a race condition if the thread is given control too early:\r
9504         we create all threads suspended so that the is->hThread variable can be\r
9505         safely assigned, then let the threads start with ResumeThread.\r
9506     */\r
9507     switch (cp->kind) {\r
9508     case CPReal:\r
9509       is->hFile = cp->hFrom;\r
9510       cp->hFrom = NULL; /* now owned by InputThread */\r
9511       is->hThread =\r
9512         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9513                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9514       break;\r
9515 \r
9516     case CPComm:\r
9517       is->hFile = cp->hFrom;\r
9518       cp->hFrom = NULL; /* now owned by InputThread */\r
9519       is->hThread =\r
9520         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9521                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9522       break;\r
9523 \r
9524     case CPSock:\r
9525       is->sock = cp->sock;\r
9526       is->hThread =\r
9527         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9528                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9529       break;\r
9530 \r
9531     case CPRcmd:\r
9532       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9533       *is2 = *is;\r
9534       is->sock = cp->sock;\r
9535       is->second = is2;\r
9536       is2->sock = cp->sock2;\r
9537       is2->second = is2;\r
9538       is->hThread =\r
9539         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9540                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9541       is2->hThread =\r
9542         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9543                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9544       break;\r
9545     }\r
9546 \r
9547     if( is->hThread != NULL ) {\r
9548         ResumeThread( is->hThread );\r
9549     }\r
9550 \r
9551     if( is2 != NULL && is2->hThread != NULL ) {\r
9552         ResumeThread( is2->hThread );\r
9553     }\r
9554   }\r
9555 \r
9556   return (InputSourceRef) is;\r
9557 }\r
9558 \r
9559 void\r
9560 RemoveInputSource(InputSourceRef isr)\r
9561 {\r
9562   InputSource *is;\r
9563 \r
9564   is = (InputSource *) isr;\r
9565   is->hThread = NULL;  /* tell thread to stop */\r
9566   CloseHandle(is->hThread);\r
9567   if (is->second != NULL) {\r
9568     is->second->hThread = NULL;\r
9569     CloseHandle(is->second->hThread);\r
9570   }\r
9571 }\r
9572 \r
9573 int no_wrap(char *message, int count)\r
9574 {\r
9575     ConsoleOutput(message, count, FALSE);\r
9576     return count;\r
9577 }\r
9578 \r
9579 int\r
9580 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9581 {\r
9582   DWORD dOutCount;\r
9583   int outCount = SOCKET_ERROR;\r
9584   ChildProc *cp = (ChildProc *) pr;\r
9585   static OVERLAPPED ovl;\r
9586   static int line = 0;\r
9587 \r
9588   if (pr == NoProc)\r
9589   {\r
9590     if (appData.noJoin || !appData.useInternalWrap)\r
9591       return no_wrap(message, count);\r
9592     else\r
9593     {\r
9594       int width = get_term_width();\r
9595       int len = wrap(NULL, message, count, width, &line);\r
9596       char *msg = malloc(len);\r
9597       int dbgchk;\r
9598 \r
9599       if (!msg)\r
9600         return no_wrap(message, count);\r
9601       else\r
9602       {\r
9603         dbgchk = wrap(msg, message, count, width, &line);\r
9604         if (dbgchk != len && appData.debugMode)\r
9605             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9606         ConsoleOutput(msg, len, FALSE);\r
9607         free(msg);\r
9608         return len;\r
9609       }\r
9610     }\r
9611   }\r
9612 \r
9613   if (ovl.hEvent == NULL) {\r
9614     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9615   }\r
9616   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9617 \r
9618   switch (cp->kind) {\r
9619   case CPSock:\r
9620   case CPRcmd:\r
9621     outCount = send(cp->sock, message, count, 0);\r
9622     if (outCount == SOCKET_ERROR) {\r
9623       *outError = WSAGetLastError();\r
9624     } else {\r
9625       *outError = NO_ERROR;\r
9626     }\r
9627     break;\r
9628 \r
9629   case CPReal:\r
9630     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9631                   &dOutCount, NULL)) {\r
9632       *outError = NO_ERROR;\r
9633       outCount = (int) dOutCount;\r
9634     } else {\r
9635       *outError = GetLastError();\r
9636     }\r
9637     break;\r
9638 \r
9639   case CPComm:\r
9640     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9641                             &dOutCount, &ovl);\r
9642     if (*outError == NO_ERROR) {\r
9643       outCount = (int) dOutCount;\r
9644     }\r
9645     break;\r
9646   }\r
9647   return outCount;\r
9648 }\r
9649 \r
9650 void\r
9651 DoSleep(int n)\r
9652 {\r
9653     if(n != 0) Sleep(n);\r
9654 }\r
9655 \r
9656 int\r
9657 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9658                        long msdelay)\r
9659 {\r
9660   /* Ignore delay, not implemented for WinBoard */\r
9661   return OutputToProcess(pr, message, count, outError);\r
9662 }\r
9663 \r
9664 \r
9665 void\r
9666 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9667                         char *buf, int count, int error)\r
9668 {\r
9669   DisplayFatalError(_("Not implemented"), 0, 1);\r
9670 }\r
9671 \r
9672 /* see wgamelist.c for Game List functions */\r
9673 /* see wedittags.c for Edit Tags functions */\r
9674 \r
9675 \r
9676 VOID\r
9677 ICSInitScript()\r
9678 {\r
9679   FILE *f;\r
9680   char buf[MSG_SIZ];\r
9681   char *dummy;\r
9682 \r
9683   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9684     f = fopen(buf, "r");\r
9685     if (f != NULL) {\r
9686       ProcessICSInitScript(f);\r
9687       fclose(f);\r
9688     }\r
9689   }\r
9690 }\r
9691 \r
9692 \r
9693 VOID\r
9694 StartAnalysisClock()\r
9695 {\r
9696   if (analysisTimerEvent) return;\r
9697   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9698                                         (UINT) 2000, NULL);\r
9699 }\r
9700 \r
9701 VOID\r
9702 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9703 {\r
9704   highlightInfo.sq[0].x = fromX;\r
9705   highlightInfo.sq[0].y = fromY;\r
9706   highlightInfo.sq[1].x = toX;\r
9707   highlightInfo.sq[1].y = toY;\r
9708 }\r
9709 \r
9710 VOID\r
9711 ClearHighlights()\r
9712 {\r
9713   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9714     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9715 }\r
9716 \r
9717 VOID\r
9718 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9719 {\r
9720   premoveHighlightInfo.sq[0].x = fromX;\r
9721   premoveHighlightInfo.sq[0].y = fromY;\r
9722   premoveHighlightInfo.sq[1].x = toX;\r
9723   premoveHighlightInfo.sq[1].y = toY;\r
9724 }\r
9725 \r
9726 VOID\r
9727 ClearPremoveHighlights()\r
9728 {\r
9729   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9730     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9731 }\r
9732 \r
9733 VOID\r
9734 ShutDownFrontEnd()\r
9735 {\r
9736   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9737   DeleteClipboardTempFiles();\r
9738 }\r
9739 \r
9740 void\r
9741 BoardToTop()\r
9742 {\r
9743     if (IsIconic(hwndMain))\r
9744       ShowWindow(hwndMain, SW_RESTORE);\r
9745 \r
9746     SetActiveWindow(hwndMain);\r
9747 }\r
9748 \r
9749 /*\r
9750  * Prototypes for animation support routines\r
9751  */\r
9752 static void ScreenSquare(int column, int row, POINT * pt);\r
9753 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9754      POINT frames[], int * nFrames);\r
9755 \r
9756 \r
9757 #define kFactor 4\r
9758 \r
9759 void\r
9760 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9761 {       // [HGM] atomic: animate blast wave\r
9762         int i;\r
9763 \r
9764         explodeInfo.fromX = fromX;\r
9765         explodeInfo.fromY = fromY;\r
9766         explodeInfo.toX = toX;\r
9767         explodeInfo.toY = toY;\r
9768         for(i=1; i<4*kFactor; i++) {\r
9769             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9770             DrawPosition(FALSE, board);\r
9771             Sleep(appData.animSpeed);\r
9772         }\r
9773         explodeInfo.radius = 0;\r
9774         DrawPosition(TRUE, board);\r
9775 }\r
9776 \r
9777 void\r
9778 AnimateMove(board, fromX, fromY, toX, toY)\r
9779      Board board;\r
9780      int fromX;\r
9781      int fromY;\r
9782      int toX;\r
9783      int toY;\r
9784 {\r
9785   ChessSquare piece;\r
9786   POINT start, finish, mid;\r
9787   POINT frames[kFactor * 2 + 1];\r
9788   int nFrames, n;\r
9789 \r
9790   if (!appData.animate) return;\r
9791   if (doingSizing) return;\r
9792   if (fromY < 0 || fromX < 0) return;\r
9793   piece = board[fromY][fromX];\r
9794   if (piece >= EmptySquare) return;\r
9795 \r
9796   ScreenSquare(fromX, fromY, &start);\r
9797   ScreenSquare(toX, toY, &finish);\r
9798 \r
9799   /* All moves except knight jumps move in straight line */\r
9800   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9801     mid.x = start.x + (finish.x - start.x) / 2;\r
9802     mid.y = start.y + (finish.y - start.y) / 2;\r
9803   } else {\r
9804     /* Knight: make straight movement then diagonal */\r
9805     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9806        mid.x = start.x + (finish.x - start.x) / 2;\r
9807        mid.y = start.y;\r
9808      } else {\r
9809        mid.x = start.x;\r
9810        mid.y = start.y + (finish.y - start.y) / 2;\r
9811      }\r
9812   }\r
9813   \r
9814   /* Don't use as many frames for very short moves */\r
9815   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9816     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9817   else\r
9818     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9819 \r
9820   animInfo.from.x = fromX;\r
9821   animInfo.from.y = fromY;\r
9822   animInfo.to.x = toX;\r
9823   animInfo.to.y = toY;\r
9824   animInfo.lastpos = start;\r
9825   animInfo.piece = piece;\r
9826   for (n = 0; n < nFrames; n++) {\r
9827     animInfo.pos = frames[n];\r
9828     DrawPosition(FALSE, NULL);\r
9829     animInfo.lastpos = animInfo.pos;\r
9830     Sleep(appData.animSpeed);\r
9831   }\r
9832   animInfo.pos = finish;\r
9833   DrawPosition(FALSE, NULL);\r
9834   animInfo.piece = EmptySquare;\r
9835   Explode(board, fromX, fromY, toX, toY);\r
9836 }\r
9837 \r
9838 /*      Convert board position to corner of screen rect and color       */\r
9839 \r
9840 static void\r
9841 ScreenSquare(column, row, pt)\r
9842      int column; int row; POINT * pt;\r
9843 {\r
9844   if (flipView) {\r
9845     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9846     pt->y = lineGap + row * (squareSize + lineGap);\r
9847   } else {\r
9848     pt->x = lineGap + column * (squareSize + lineGap);\r
9849     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9850   }\r
9851 }\r
9852 \r
9853 /*      Generate a series of frame coords from start->mid->finish.\r
9854         The movement rate doubles until the half way point is\r
9855         reached, then halves back down to the final destination,\r
9856         which gives a nice slow in/out effect. The algorithmn\r
9857         may seem to generate too many intermediates for short\r
9858         moves, but remember that the purpose is to attract the\r
9859         viewers attention to the piece about to be moved and\r
9860         then to where it ends up. Too few frames would be less\r
9861         noticeable.                                             */\r
9862 \r
9863 static void\r
9864 Tween(start, mid, finish, factor, frames, nFrames)\r
9865      POINT * start; POINT * mid;\r
9866      POINT * finish; int factor;\r
9867      POINT frames[]; int * nFrames;\r
9868 {\r
9869   int n, fraction = 1, count = 0;\r
9870 \r
9871   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9872   for (n = 0; n < factor; n++)\r
9873     fraction *= 2;\r
9874   for (n = 0; n < factor; n++) {\r
9875     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9876     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9877     count ++;\r
9878     fraction = fraction / 2;\r
9879   }\r
9880   \r
9881   /* Midpoint */\r
9882   frames[count] = *mid;\r
9883   count ++;\r
9884   \r
9885   /* Slow out, stepping 1/2, then 1/4, ... */\r
9886   fraction = 2;\r
9887   for (n = 0; n < factor; n++) {\r
9888     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9889     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9890     count ++;\r
9891     fraction = fraction * 2;\r
9892   }\r
9893   *nFrames = count;\r
9894 }\r
9895 \r
9896 void\r
9897 SettingsPopUp(ChessProgramState *cps)\r
9898 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9899       EngineOptionsPopup(savedHwnd, cps);\r
9900 }\r
9901 \r
9902 int flock(int fid, int code)\r
9903 {\r
9904     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9905     OVERLAPPED ov;\r
9906     ov.hEvent = NULL;\r
9907     ov.Offset = 0;\r
9908     ov.OffsetHigh = 0;\r
9909     switch(code) {\r
9910       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9911       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9912       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9913       default: return -1;\r
9914     }\r
9915     return 0;\r
9916 }\r