Use combobox line for recent engines when available
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][42] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
257   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
318   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
319   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
322   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
323 { DLG_MoveHistory }, \r
324 { DLG_EvalGraph }, \r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
328   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
329   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
330   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
332   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
333   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
334 { 0 }\r
335 };\r
336 \r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
338 static int lastChecked;\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
340 extern int tinyLayout;\r
341 extern char * menuBarText[][10];\r
342 \r
343 void\r
344 LoadLanguageFile(char *name)\r
345 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
346     FILE *f;\r
347     int i=0, j=0, n=0, k;\r
348     char buf[MSG_SIZ];\r
349 \r
350     if(!name || name[0] == NULLCHAR) return;\r
351       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
352     appData.language = oldLanguage;\r
353     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
354     if((f = fopen(buf, "r")) == NULL) return;\r
355     while((k = fgetc(f)) != EOF) {\r
356         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
357         languageBuf[i] = k;\r
358         if(k == '\n') {\r
359             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
360                 char *p;\r
361                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
362                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
363                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
364                         english[j] = languageBuf + n + 1; *p = 0;\r
365                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
367                     }\r
368                 }\r
369             }\r
370             n = i + 1;\r
371         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
372             switch(k) {\r
373               case 'n': k = '\n'; break;\r
374               case 'r': k = '\r'; break;\r
375               case 't': k = '\t'; break;\r
376             }\r
377             languageBuf[--i] = k;\r
378         }\r
379         i++;\r
380     }\r
381     fclose(f);\r
382     barbaric = (j != 0);\r
383     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
384 }\r
385 \r
386 char *\r
387 T_(char *s)\r
388 {   // return the translation of the given string\r
389     // efficiency can be improved a lot...\r
390     int i=0;\r
391     static char buf[MSG_SIZ];\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
393     if(!barbaric) return s;\r
394     if(!s) return ""; // sanity\r
395     while(english[i]) {\r
396         if(!strcmp(s, english[i])) return foreign[i];\r
397         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
398             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
399             return buf;\r
400         }\r
401         i++;\r
402     }\r
403     return s;\r
404 }\r
405 \r
406 void\r
407 Translate(HWND hDlg, int dialogID)\r
408 {   // translate all text items in the given dialog\r
409     int i=0, j, k;\r
410     char buf[MSG_SIZ], *s;\r
411     if(!barbaric) return;\r
412     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
413     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
414     GetWindowText( hDlg, buf, MSG_SIZ );\r
415     s = T_(buf);\r
416     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
417     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
418         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
419         if(strlen(buf) == 0) continue;\r
420         s = T_(buf);\r
421         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
422     }\r
423 }\r
424 \r
425 HMENU\r
426 TranslateOneMenu(int i, HMENU subMenu)\r
427 {\r
428     int j;\r
429     static MENUITEMINFO info;\r
430 \r
431     info.cbSize = sizeof(MENUITEMINFO);\r
432     info.fMask = MIIM_STATE | MIIM_TYPE;\r
433           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
434             char buf[MSG_SIZ];\r
435             info.dwTypeData = buf;\r
436             info.cch = sizeof(buf);\r
437             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
438             if(i < 10) {\r
439                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
440                 else menuText[i][j] = strdup(buf); // remember original on first change\r
441             }\r
442             if(buf[0] == NULLCHAR) continue;\r
443             info.dwTypeData = T_(buf);\r
444             info.cch = strlen(buf)+1;\r
445             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
446           }\r
447     return subMenu;\r
448 }\r
449 \r
450 void\r
451 TranslateMenus(int addLanguage)\r
452 {\r
453     int i;\r
454     WIN32_FIND_DATA fileData;\r
455     HANDLE hFind;\r
456 #define IDM_English 1970\r
457     if(1) {\r
458         HMENU mainMenu = GetMenu(hwndMain);\r
459         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
460           HMENU subMenu = GetSubMenu(mainMenu, i);\r
461           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
462                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
463           TranslateOneMenu(i, subMenu);\r
464         }\r
465         DrawMenuBar(hwndMain);\r
466     }\r
467 \r
468     if(!addLanguage) return;\r
469     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
470         HMENU mainMenu = GetMenu(hwndMain);\r
471         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
472         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
473         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
474         i = 0; lastChecked = IDM_English;\r
475         do {\r
476             char *p, *q = fileData.cFileName;\r
477             int checkFlag = MF_UNCHECKED;\r
478             languageFile[i] = strdup(q);\r
479             if(barbaric && !strcmp(oldLanguage, q)) {\r
480                 checkFlag = MF_CHECKED;\r
481                 lastChecked = IDM_English + i + 1;\r
482                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
483             }\r
484             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
485             p = strstr(fileData.cFileName, ".lng");\r
486             if(p) *p = 0;\r
487             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
488         } while(FindNextFile(hFind, &fileData));\r
489         FindClose(hFind);\r
490     }\r
491 }\r
492 \r
493 #endif\r
494 \r
495 #define IDM_RecentEngines 3000\r
496 \r
497 void\r
498 RecentEngineMenu (char *s)\r
499 {\r
500     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
501         HMENU mainMenu = GetMenu(hwndMain);\r
502         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
503         int i=IDM_RecentEngines;\r
504         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
505         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
506         while(*s) {\r
507           char *p = strchr(s, '\n');\r
508           if(p == NULL) return; // malformed!\r
509           *p = NULLCHAR;\r
510           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
511           *p = '\n';\r
512           s = p+1;\r
513         }\r
514     }\r
515 }\r
516 \r
517 \r
518 typedef struct {\r
519   char *name;\r
520   int squareSize;\r
521   int lineGap;\r
522   int smallLayout;\r
523   int tinyLayout;\r
524   int cliWidth, cliHeight;\r
525 } SizeInfo;\r
526 \r
527 SizeInfo sizeInfo[] = \r
528 {\r
529   { "tiny",     21, 0, 1, 1, 0, 0 },\r
530   { "teeny",    25, 1, 1, 1, 0, 0 },\r
531   { "dinky",    29, 1, 1, 1, 0, 0 },\r
532   { "petite",   33, 1, 1, 1, 0, 0 },\r
533   { "slim",     37, 2, 1, 0, 0, 0 },\r
534   { "small",    40, 2, 1, 0, 0, 0 },\r
535   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
536   { "middling", 49, 2, 0, 0, 0, 0 },\r
537   { "average",  54, 2, 0, 0, 0, 0 },\r
538   { "moderate", 58, 3, 0, 0, 0, 0 },\r
539   { "medium",   64, 3, 0, 0, 0, 0 },\r
540   { "bulky",    72, 3, 0, 0, 0, 0 },\r
541   { "large",    80, 3, 0, 0, 0, 0 },\r
542   { "big",      87, 3, 0, 0, 0, 0 },\r
543   { "huge",     95, 3, 0, 0, 0, 0 },\r
544   { "giant",    108, 3, 0, 0, 0, 0 },\r
545   { "colossal", 116, 4, 0, 0, 0, 0 },\r
546   { "titanic",  129, 4, 0, 0, 0, 0 },\r
547   { NULL, 0, 0, 0, 0, 0, 0 }\r
548 };\r
549 \r
550 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
551 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
552 {\r
553   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
554   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
555   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
556   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
557   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
558   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
559   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
560   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
561   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571 };\r
572 \r
573 MyFont *font[NUM_SIZES][NUM_FONTS];\r
574 \r
575 typedef struct {\r
576   char *label;\r
577   int id;\r
578   HWND hwnd;\r
579   WNDPROC wndproc;\r
580 } MyButtonDesc;\r
581 \r
582 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
583 #define N_BUTTONS 5\r
584 \r
585 MyButtonDesc buttonDesc[N_BUTTONS] =\r
586 {\r
587   {"<<", IDM_ToStart, NULL, NULL},\r
588   {"<", IDM_Backward, NULL, NULL},\r
589   {"P", IDM_Pause, NULL, NULL},\r
590   {">", IDM_Forward, NULL, NULL},\r
591   {">>", IDM_ToEnd, NULL, NULL},\r
592 };\r
593 \r
594 int tinyLayout = 0, smallLayout = 0;\r
595 #define MENU_BAR_ITEMS 9\r
596 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
597   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
598   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
599 };\r
600 \r
601 \r
602 MySound sounds[(int)NSoundClasses];\r
603 MyTextAttribs textAttribs[(int)NColorClasses];\r
604 \r
605 MyColorizeAttribs colorizeAttribs[] = {\r
606   { (COLORREF)0, 0, N_("Shout Text") },\r
607   { (COLORREF)0, 0, N_("SShout/CShout") },\r
608   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
609   { (COLORREF)0, 0, N_("Channel Text") },\r
610   { (COLORREF)0, 0, N_("Kibitz Text") },\r
611   { (COLORREF)0, 0, N_("Tell Text") },\r
612   { (COLORREF)0, 0, N_("Challenge Text") },\r
613   { (COLORREF)0, 0, N_("Request Text") },\r
614   { (COLORREF)0, 0, N_("Seek Text") },\r
615   { (COLORREF)0, 0, N_("Normal Text") },\r
616   { (COLORREF)0, 0, N_("None") }\r
617 };\r
618 \r
619 \r
620 \r
621 static char *commentTitle;\r
622 static char *commentText;\r
623 static int commentIndex;\r
624 static Boolean editComment = FALSE;\r
625 \r
626 \r
627 char errorTitle[MSG_SIZ];\r
628 char errorMessage[2*MSG_SIZ];\r
629 HWND errorDialog = NULL;\r
630 BOOLEAN moveErrorMessageUp = FALSE;\r
631 BOOLEAN consoleEcho = TRUE;\r
632 CHARFORMAT consoleCF;\r
633 COLORREF consoleBackgroundColor;\r
634 \r
635 char *programVersion;\r
636 \r
637 #define CPReal 1\r
638 #define CPComm 2\r
639 #define CPSock 3\r
640 #define CPRcmd 4\r
641 typedef int CPKind;\r
642 \r
643 typedef struct {\r
644   CPKind kind;\r
645   HANDLE hProcess;\r
646   DWORD pid;\r
647   HANDLE hTo;\r
648   HANDLE hFrom;\r
649   SOCKET sock;\r
650   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
651 } ChildProc;\r
652 \r
653 #define INPUT_SOURCE_BUF_SIZE 4096\r
654 \r
655 typedef struct _InputSource {\r
656   CPKind kind;\r
657   HANDLE hFile;\r
658   SOCKET sock;\r
659   int lineByLine;\r
660   HANDLE hThread;\r
661   DWORD id;\r
662   char buf[INPUT_SOURCE_BUF_SIZE];\r
663   char *next;\r
664   DWORD count;\r
665   int error;\r
666   InputCallback func;\r
667   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
668   VOIDSTAR closure;\r
669 } InputSource;\r
670 \r
671 InputSource *consoleInputSource;\r
672 \r
673 DCB dcb;\r
674 \r
675 /* forward */\r
676 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
677 VOID ConsoleCreate();\r
678 LRESULT CALLBACK\r
679   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
680 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
681 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
682 VOID ParseCommSettings(char *arg, DCB *dcb);\r
683 LRESULT CALLBACK\r
684   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
685 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
686 void ParseIcsTextMenu(char *icsTextMenuString);\r
687 VOID PopUpNameDialog(char firstchar);\r
688 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
689 \r
690 /* [AS] */\r
691 int NewGameFRC();\r
692 int GameListOptions();\r
693 \r
694 int dummy; // [HGM] for obsolete args\r
695 \r
696 HWND hwndMain = NULL;        /* root window*/\r
697 HWND hwndConsole = NULL;\r
698 HWND commentDialog = NULL;\r
699 HWND moveHistoryDialog = NULL;\r
700 HWND evalGraphDialog = NULL;\r
701 HWND engineOutputDialog = NULL;\r
702 HWND gameListDialog = NULL;\r
703 HWND editTagsDialog = NULL;\r
704 \r
705 int commentUp = FALSE;\r
706 \r
707 WindowPlacement wpMain;\r
708 WindowPlacement wpConsole;\r
709 WindowPlacement wpComment;\r
710 WindowPlacement wpMoveHistory;\r
711 WindowPlacement wpEvalGraph;\r
712 WindowPlacement wpEngineOutput;\r
713 WindowPlacement wpGameList;\r
714 WindowPlacement wpTags;\r
715 \r
716 VOID EngineOptionsPopup(); // [HGM] settings\r
717 \r
718 VOID GothicPopUp(char *title, VariantClass variant);\r
719 /*\r
720  * Setting "frozen" should disable all user input other than deleting\r
721  * the window.  We do this while engines are initializing themselves.\r
722  */\r
723 static int frozen = 0;\r
724 static int oldMenuItemState[MENU_BAR_ITEMS];\r
725 void FreezeUI()\r
726 {\r
727   HMENU hmenu;\r
728   int i;\r
729 \r
730   if (frozen) return;\r
731   frozen = 1;\r
732   hmenu = GetMenu(hwndMain);\r
733   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
734     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
735   }\r
736   DrawMenuBar(hwndMain);\r
737 }\r
738 \r
739 /* Undo a FreezeUI */\r
740 void ThawUI()\r
741 {\r
742   HMENU hmenu;\r
743   int i;\r
744 \r
745   if (!frozen) return;\r
746   frozen = 0;\r
747   hmenu = GetMenu(hwndMain);\r
748   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
749     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
750   }\r
751   DrawMenuBar(hwndMain);\r
752 }\r
753 \r
754 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
755 \r
756 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
757 #ifdef JAWS\r
758 #include "jaws.c"\r
759 #else\r
760 #define JAWS_INIT\r
761 #define JAWS_ARGS\r
762 #define JAWS_ALT_INTERCEPT\r
763 #define JAWS_KBUP_NAVIGATION\r
764 #define JAWS_KBDOWN_NAVIGATION\r
765 #define JAWS_MENU_ITEMS\r
766 #define JAWS_SILENCE\r
767 #define JAWS_REPLAY\r
768 #define JAWS_ACCEL\r
769 #define JAWS_COPYRIGHT\r
770 #define JAWS_DELETE(X) X\r
771 #define SAYMACHINEMOVE()\r
772 #define SAY(X)\r
773 #endif\r
774 \r
775 /*---------------------------------------------------------------------------*\\r
776  *\r
777  * WinMain\r
778  *\r
779 \*---------------------------------------------------------------------------*/\r
780 \r
781 int APIENTRY\r
782 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
783         LPSTR lpCmdLine, int nCmdShow)\r
784 {\r
785   MSG msg;\r
786   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
787 //  INITCOMMONCONTROLSEX ex;\r
788 \r
789   debugFP = stderr;\r
790 \r
791   LoadLibrary("RICHED32.DLL");\r
792   consoleCF.cbSize = sizeof(CHARFORMAT);\r
793 \r
794   if (!InitApplication(hInstance)) {\r
795     return (FALSE);\r
796   }\r
797   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
798     return (FALSE);\r
799   }\r
800 \r
801   JAWS_INIT\r
802   TranslateMenus(1);\r
803 \r
804 //  InitCommonControlsEx(&ex);\r
805   InitCommonControls();\r
806 \r
807   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
808   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
809   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
810 \r
811   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
812 \r
813   while (GetMessage(&msg, /* message structure */\r
814                     NULL, /* handle of window receiving the message */\r
815                     0,    /* lowest message to examine */\r
816                     0))   /* highest message to examine */\r
817     {\r
818 \r
819       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
820         // [HGM] navigate: switch between all windows with tab\r
821         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
822         int i, currentElement = 0;\r
823 \r
824         // first determine what element of the chain we come from (if any)\r
825         if(appData.icsActive) {\r
826             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
827             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
828         }\r
829         if(engineOutputDialog && EngineOutputIsUp()) {\r
830             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
831             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
832         }\r
833         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
834             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
835         }\r
836         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
837         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
838         if(msg.hwnd == e1)                 currentElement = 2; else\r
839         if(msg.hwnd == e2)                 currentElement = 3; else\r
840         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
841         if(msg.hwnd == mh)                currentElement = 4; else\r
842         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
843         if(msg.hwnd == hText)  currentElement = 5; else\r
844         if(msg.hwnd == hInput) currentElement = 6; else\r
845         for (i = 0; i < N_BUTTONS; i++) {\r
846             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
847         }\r
848 \r
849         // determine where to go to\r
850         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
851           do {\r
852             currentElement = (currentElement + direction) % 7;\r
853             switch(currentElement) {\r
854                 case 0:\r
855                   h = hwndMain; break; // passing this case always makes the loop exit\r
856                 case 1:\r
857                   h = buttonDesc[0].hwnd; break; // could be NULL\r
858                 case 2:\r
859                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
860                   h = e1; break;\r
861                 case 3:\r
862                   if(!EngineOutputIsUp()) continue;\r
863                   h = e2; break;\r
864                 case 4:\r
865                   if(!MoveHistoryIsUp()) continue;\r
866                   h = mh; break;\r
867 //              case 6: // input to eval graph does not seem to get here!\r
868 //                if(!EvalGraphIsUp()) continue;\r
869 //                h = evalGraphDialog; break;\r
870                 case 5:\r
871                   if(!appData.icsActive) continue;\r
872                   SAY("display");\r
873                   h = hText; break;\r
874                 case 6:\r
875                   if(!appData.icsActive) continue;\r
876                   SAY("input");\r
877                   h = hInput; break;\r
878             }\r
879           } while(h == 0);\r
880 \r
881           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
882           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
883           SetFocus(h);\r
884 \r
885           continue; // this message now has been processed\r
886         }\r
887       }\r
888 \r
889       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
890           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
891           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
892           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
893           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
894           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
895           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
896           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
897           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
898           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
899         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
900         for(i=0; i<MAX_CHAT; i++) \r
901             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
902                 done = 1; break;\r
903         }\r
904         if(done) continue; // [HGM] chat: end patch\r
905         TranslateMessage(&msg); /* Translates virtual key codes */\r
906         DispatchMessage(&msg);  /* Dispatches message to window */\r
907       }\r
908     }\r
909 \r
910 \r
911   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
912 }\r
913 \r
914 /*---------------------------------------------------------------------------*\\r
915  *\r
916  * Initialization functions\r
917  *\r
918 \*---------------------------------------------------------------------------*/\r
919 \r
920 void\r
921 SetUserLogo()\r
922 {   // update user logo if necessary\r
923     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
924 \r
925     if(appData.autoLogo) {\r
926           curName = UserName();\r
927           if(strcmp(curName, oldUserName)) {\r
928                 GetCurrentDirectory(MSG_SIZ, dir);\r
929                 SetCurrentDirectory(installDir);\r
930                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
931                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
932                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
933                 if(userLogo == NULL)\r
934                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
935                 SetCurrentDirectory(dir); /* return to prev directory */\r
936           }\r
937     }\r
938 }\r
939 \r
940 BOOL\r
941 InitApplication(HINSTANCE hInstance)\r
942 {\r
943   WNDCLASS wc;\r
944 \r
945   /* Fill in window class structure with parameters that describe the */\r
946   /* main window. */\r
947 \r
948   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
949   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
950   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
951   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
952   wc.hInstance     = hInstance;         /* Owner of this class */\r
953   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
954   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
955   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
956   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
957   wc.lpszClassName = szAppName;                 /* Name to register as */\r
958 \r
959   /* Register the window class and return success/failure code. */\r
960   if (!RegisterClass(&wc)) return FALSE;\r
961 \r
962   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
963   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
964   wc.cbClsExtra    = 0;\r
965   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
966   wc.hInstance     = hInstance;\r
967   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
968   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
969   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
970   wc.lpszMenuName  = NULL;\r
971   wc.lpszClassName = szConsoleName;\r
972 \r
973   if (!RegisterClass(&wc)) return FALSE;\r
974   return TRUE;\r
975 }\r
976 \r
977 \r
978 /* Set by InitInstance, used by EnsureOnScreen */\r
979 int screenHeight, screenWidth;\r
980 \r
981 void\r
982 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
983 {\r
984 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
985   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
986   if (*x > screenWidth - 32) *x = 0;\r
987   if (*y > screenHeight - 32) *y = 0;\r
988   if (*x < minX) *x = minX;\r
989   if (*y < minY) *y = minY;\r
990 }\r
991 \r
992 VOID\r
993 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
994 {\r
995   char buf[MSG_SIZ], dir[MSG_SIZ];\r
996   GetCurrentDirectory(MSG_SIZ, dir);\r
997   SetCurrentDirectory(installDir);\r
998   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
999       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (cps->programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1006         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1007         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1008       } else\r
1009       if(appData.directory[n] && appData.directory[n][0]) {\r
1010         SetCurrentDirectory(appData.directory[n]);\r
1011         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1012       }\r
1013   }\r
1014   SetCurrentDirectory(dir); /* return to prev directory */\r
1015 }\r
1016 \r
1017 VOID\r
1018 InitTextures()\r
1019 {\r
1020   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1021   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1022   \r
1023   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1024       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1025       liteBackTextureMode = appData.liteBackTextureMode;\r
1026 \r
1027       if (liteBackTexture == NULL && appData.debugMode) {\r
1028           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1029       }\r
1030   }\r
1031   \r
1032   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1033       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1034       darkBackTextureMode = appData.darkBackTextureMode;\r
1035 \r
1036       if (darkBackTexture == NULL && appData.debugMode) {\r
1037           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1038       }\r
1039   }\r
1040 }\r
1041 \r
1042 BOOL\r
1043 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1044 {\r
1045   HWND hwnd; /* Main window handle. */\r
1046   int ibs;\r
1047   WINDOWPLACEMENT wp;\r
1048   char *filepart;\r
1049 \r
1050   hInst = hInstance;    /* Store instance handle in our global variable */\r
1051   programName = szAppName;\r
1052 \r
1053   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1054     *filepart = NULLCHAR;\r
1055   } else {\r
1056     GetCurrentDirectory(MSG_SIZ, installDir);\r
1057   }\r
1058   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1059   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1060   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1061   /* xboard, and older WinBoards, controlled the move sound with the\r
1062      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1063      always turn the option on (so that the backend will call us),\r
1064      then let the user turn the sound off by setting it to silence if\r
1065      desired.  To accommodate old winboard.ini files saved by old\r
1066      versions of WinBoard, we also turn off the sound if the option\r
1067      was initially set to false. [HGM] taken out of InitAppData */\r
1068   if (!appData.ringBellAfterMoves) {\r
1069     sounds[(int)SoundMove].name = strdup("");\r
1070     appData.ringBellAfterMoves = TRUE;\r
1071   }\r
1072   if (appData.debugMode) {\r
1073     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1074     setbuf(debugFP, NULL);\r
1075   }\r
1076 \r
1077   LoadLanguageFile(appData.language);\r
1078 \r
1079   InitBackEnd1();\r
1080 \r
1081 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1082 //  InitEngineUCI( installDir, &second );\r
1083 \r
1084   /* Create a main window for this application instance. */\r
1085   hwnd = CreateWindow(szAppName, szTitle,\r
1086                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1087                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1088                       NULL, NULL, hInstance, NULL);\r
1089   hwndMain = hwnd;\r
1090 \r
1091   /* If window could not be created, return "failure" */\r
1092   if (!hwnd) {\r
1093     return (FALSE);\r
1094   }\r
1095 \r
1096   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1097   LoadLogo(&first, 0, FALSE);\r
1098   LoadLogo(&second, 1, appData.icsActive);\r
1099 \r
1100   SetUserLogo();\r
1101 \r
1102   iconWhite = LoadIcon(hInstance, "icon_white");\r
1103   iconBlack = LoadIcon(hInstance, "icon_black");\r
1104   iconCurrent = iconWhite;\r
1105   InitDrawingColors();\r
1106   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1107   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1108   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1109     /* Compute window size for each board size, and use the largest\r
1110        size that fits on this screen as the default. */\r
1111     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1112     if (boardSize == (BoardSize)-1 &&\r
1113         winH <= screenHeight\r
1114            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1115         && winW <= screenWidth) {\r
1116       boardSize = (BoardSize)ibs;\r
1117     }\r
1118   }\r
1119 \r
1120   InitDrawingSizes(boardSize, 0);\r
1121   RecentEngineMenu(appData.recentEngineList);\r
1122   InitMenuChecks();\r
1123   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1124 \r
1125   /* [AS] Load textures if specified */\r
1126   InitTextures();\r
1127 \r
1128   mysrandom( (unsigned) time(NULL) );\r
1129 \r
1130   /* [AS] Restore layout */\r
1131   if( wpMoveHistory.visible ) {\r
1132       MoveHistoryPopUp();\r
1133   }\r
1134 \r
1135   if( wpEvalGraph.visible ) {\r
1136       EvalGraphPopUp();\r
1137   }\r
1138 \r
1139   if( wpEngineOutput.visible ) {\r
1140       EngineOutputPopUp();\r
1141   }\r
1142 \r
1143   /* Make the window visible; update its client area; and return "success" */\r
1144   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1145   wp.length = sizeof(WINDOWPLACEMENT);\r
1146   wp.flags = 0;\r
1147   wp.showCmd = nCmdShow;\r
1148   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1149   wp.rcNormalPosition.left = wpMain.x;\r
1150   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1151   wp.rcNormalPosition.top = wpMain.y;\r
1152   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1153   SetWindowPlacement(hwndMain, &wp);\r
1154 \r
1155   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1156 \r
1157   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1158                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1159 \r
1160   if (hwndConsole) {\r
1161 #if AOT_CONSOLE\r
1162     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1163                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1164 #endif\r
1165     ShowWindow(hwndConsole, nCmdShow);\r
1166     SetActiveWindow(hwndConsole);\r
1167   }\r
1168   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1169   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1170 \r
1171   return TRUE;\r
1172 \r
1173 }\r
1174 \r
1175 VOID\r
1176 InitMenuChecks()\r
1177 {\r
1178   HMENU hmenu = GetMenu(hwndMain);\r
1179 \r
1180   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1181                         MF_BYCOMMAND|((appData.icsActive &&\r
1182                                        *appData.icsCommPort != NULLCHAR) ?\r
1183                                       MF_ENABLED : MF_GRAYED));\r
1184   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1185                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1186                                      MF_CHECKED : MF_UNCHECKED));\r
1187 }\r
1188 \r
1189 //---------------------------------------------------------------------------------------------------------\r
1190 \r
1191 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1192 #define XBOARD FALSE\r
1193 \r
1194 #define OPTCHAR "/"\r
1195 #define SEPCHAR "="\r
1196 \r
1197 #include "args.h"\r
1198 \r
1199 // front-end part of option handling\r
1200 \r
1201 VOID\r
1202 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1203 {\r
1204   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1205   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1206   DeleteDC(hdc);\r
1207   lf->lfWidth = 0;\r
1208   lf->lfEscapement = 0;\r
1209   lf->lfOrientation = 0;\r
1210   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1211   lf->lfItalic = mfp->italic;\r
1212   lf->lfUnderline = mfp->underline;\r
1213   lf->lfStrikeOut = mfp->strikeout;\r
1214   lf->lfCharSet = mfp->charset;\r
1215   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1216   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1217   lf->lfQuality = DEFAULT_QUALITY;\r
1218   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1219     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1220 }\r
1221 \r
1222 void\r
1223 CreateFontInMF(MyFont *mf)\r
1224\r
1225   LFfromMFP(&mf->lf, &mf->mfp);\r
1226   if (mf->hf) DeleteObject(mf->hf);\r
1227   mf->hf = CreateFontIndirect(&mf->lf);\r
1228 }\r
1229 \r
1230 // [HGM] This platform-dependent table provides the location for storing the color info\r
1231 void *\r
1232 colorVariable[] = {\r
1233   &whitePieceColor, \r
1234   &blackPieceColor, \r
1235   &lightSquareColor,\r
1236   &darkSquareColor, \r
1237   &highlightSquareColor,\r
1238   &premoveHighlightColor,\r
1239   NULL,\r
1240   &consoleBackgroundColor,\r
1241   &appData.fontForeColorWhite,\r
1242   &appData.fontBackColorWhite,\r
1243   &appData.fontForeColorBlack,\r
1244   &appData.fontBackColorBlack,\r
1245   &appData.evalHistColorWhite,\r
1246   &appData.evalHistColorBlack,\r
1247   &appData.highlightArrowColor,\r
1248 };\r
1249 \r
1250 /* Command line font name parser.  NULL name means do nothing.\r
1251    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1252    For backward compatibility, syntax without the colon is also\r
1253    accepted, but font names with digits in them won't work in that case.\r
1254 */\r
1255 VOID\r
1256 ParseFontName(char *name, MyFontParams *mfp)\r
1257 {\r
1258   char *p, *q;\r
1259   if (name == NULL) return;\r
1260   p = name;\r
1261   q = strchr(p, ':');\r
1262   if (q) {\r
1263     if (q - p >= sizeof(mfp->faceName))\r
1264       ExitArgError(_("Font name too long:"), name, TRUE);\r
1265     memcpy(mfp->faceName, p, q - p);\r
1266     mfp->faceName[q - p] = NULLCHAR;\r
1267     p = q + 1;\r
1268   } else {\r
1269     q = mfp->faceName;\r
1270     while (*p && !isdigit(*p)) {\r
1271       *q++ = *p++;\r
1272       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1273         ExitArgError(_("Font name too long:"), name, TRUE);\r
1274     }\r
1275     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1276     *q = NULLCHAR;\r
1277   }\r
1278   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1279   mfp->pointSize = (float) atof(p);\r
1280   mfp->bold = (strchr(p, 'b') != NULL);\r
1281   mfp->italic = (strchr(p, 'i') != NULL);\r
1282   mfp->underline = (strchr(p, 'u') != NULL);\r
1283   mfp->strikeout = (strchr(p, 's') != NULL);\r
1284   mfp->charset = DEFAULT_CHARSET;\r
1285   q = strchr(p, 'c');\r
1286   if (q)\r
1287     mfp->charset = (BYTE) atoi(q+1);\r
1288 }\r
1289 \r
1290 void\r
1291 ParseFont(char *name, int number)\r
1292 { // wrapper to shield back-end from 'font'\r
1293   ParseFontName(name, &font[boardSize][number]->mfp);\r
1294 }\r
1295 \r
1296 void\r
1297 SetFontDefaults()\r
1298 { // in WB  we have a 2D array of fonts; this initializes their description\r
1299   int i, j;\r
1300   /* Point font array elements to structures and\r
1301      parse default font names */\r
1302   for (i=0; i<NUM_FONTS; i++) {\r
1303     for (j=0; j<NUM_SIZES; j++) {\r
1304       font[j][i] = &fontRec[j][i];\r
1305       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1306     }\r
1307   }\r
1308 }\r
1309 \r
1310 void\r
1311 CreateFonts()\r
1312 { // here we create the actual fonts from the selected descriptions\r
1313   int i, j;\r
1314   for (i=0; i<NUM_FONTS; i++) {\r
1315     for (j=0; j<NUM_SIZES; j++) {\r
1316       CreateFontInMF(font[j][i]);\r
1317     }\r
1318   }\r
1319 }\r
1320 /* Color name parser.\r
1321    X version accepts X color names, but this one\r
1322    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1323 COLORREF\r
1324 ParseColorName(char *name)\r
1325 {\r
1326   int red, green, blue, count;\r
1327   char buf[MSG_SIZ];\r
1328 \r
1329   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1330   if (count != 3) {\r
1331     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1332       &red, &green, &blue);\r
1333   }\r
1334   if (count != 3) {\r
1335     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1336     DisplayError(buf, 0);\r
1337     return RGB(0, 0, 0);\r
1338   }\r
1339   return PALETTERGB(red, green, blue);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseColor(int n, char *name)\r
1344 { // for WinBoard the color is an int, which needs to be derived from the string\r
1345   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1346 }\r
1347 \r
1348 void\r
1349 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1350 {\r
1351   char *e = argValue;\r
1352   int eff = 0;\r
1353 \r
1354   while (*e) {\r
1355     if (*e == 'b')      eff |= CFE_BOLD;\r
1356     else if (*e == 'i') eff |= CFE_ITALIC;\r
1357     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1358     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1359     else if (*e == '#' || isdigit(*e)) break;\r
1360     e++;\r
1361   }\r
1362   *effects = eff;\r
1363   *color   = ParseColorName(e);\r
1364 }\r
1365 \r
1366 void\r
1367 ParseTextAttribs(ColorClass cc, char *s)\r
1368 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1369     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1370     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1371 }\r
1372 \r
1373 void\r
1374 ParseBoardSize(void *addr, char *name)\r
1375 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1376   BoardSize bs = SizeTiny;\r
1377   while (sizeInfo[bs].name != NULL) {\r
1378     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1379         *(BoardSize *)addr = bs;\r
1380         return;\r
1381     }\r
1382     bs++;\r
1383   }\r
1384   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1385 }\r
1386 \r
1387 void\r
1388 LoadAllSounds()\r
1389 { // [HGM] import name from appData first\r
1390   ColorClass cc;\r
1391   SoundClass sc;\r
1392   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1393     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1394     textAttribs[cc].sound.data = NULL;\r
1395     MyLoadSound(&textAttribs[cc].sound);\r
1396   }\r
1397   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1398     textAttribs[cc].sound.name = strdup("");\r
1399     textAttribs[cc].sound.data = NULL;\r
1400   }\r
1401   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1402     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1403     sounds[sc].data = NULL;\r
1404     MyLoadSound(&sounds[sc]);\r
1405   }\r
1406 }\r
1407 \r
1408 void\r
1409 SetCommPortDefaults()\r
1410 {\r
1411    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1412   dcb.DCBlength = sizeof(DCB);\r
1413   dcb.BaudRate = 9600;\r
1414   dcb.fBinary = TRUE;\r
1415   dcb.fParity = FALSE;\r
1416   dcb.fOutxCtsFlow = FALSE;\r
1417   dcb.fOutxDsrFlow = FALSE;\r
1418   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1419   dcb.fDsrSensitivity = FALSE;\r
1420   dcb.fTXContinueOnXoff = TRUE;\r
1421   dcb.fOutX = FALSE;\r
1422   dcb.fInX = FALSE;\r
1423   dcb.fNull = FALSE;\r
1424   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1425   dcb.fAbortOnError = FALSE;\r
1426   dcb.ByteSize = 7;\r
1427   dcb.Parity = SPACEPARITY;\r
1428   dcb.StopBits = ONESTOPBIT;\r
1429 }\r
1430 \r
1431 // [HGM] args: these three cases taken out to stay in front-end\r
1432 void\r
1433 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1434 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1435         // while the curent board size determines the element. This system should be ported to XBoard.\r
1436         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1437         int bs;\r
1438         for (bs=0; bs<NUM_SIZES; bs++) {\r
1439           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1440           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1441           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1442             ad->argName, mfp->faceName, mfp->pointSize,\r
1443             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1444             mfp->bold ? "b" : "",\r
1445             mfp->italic ? "i" : "",\r
1446             mfp->underline ? "u" : "",\r
1447             mfp->strikeout ? "s" : "",\r
1448             (int)mfp->charset);\r
1449         }\r
1450       }\r
1451 \r
1452 void\r
1453 ExportSounds()\r
1454 { // [HGM] copy the names from the internal WB variables to appData\r
1455   ColorClass cc;\r
1456   SoundClass sc;\r
1457   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1458     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1459   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1460     (&appData.soundMove)[sc] = sounds[sc].name;\r
1461 }\r
1462 \r
1463 void\r
1464 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1465 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1466         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1467         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1468           (ta->effects & CFE_BOLD) ? "b" : "",\r
1469           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1470           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1471           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1472           (ta->effects) ? " " : "",\r
1473           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1474       }\r
1475 \r
1476 void\r
1477 SaveColor(FILE *f, ArgDescriptor *ad)\r
1478 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1479         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1480         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1481           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1482 }\r
1483 \r
1484 void\r
1485 SaveBoardSize(FILE *f, char *name, void *addr)\r
1486 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1487   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1488 }\r
1489 \r
1490 void\r
1491 ParseCommPortSettings(char *s)\r
1492 { // wrapper to keep dcb from back-end\r
1493   ParseCommSettings(s, &dcb);\r
1494 }\r
1495 \r
1496 void\r
1497 GetWindowCoords()\r
1498 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1499   GetActualPlacement(hwndMain, &wpMain);\r
1500   GetActualPlacement(hwndConsole, &wpConsole);\r
1501   GetActualPlacement(commentDialog, &wpComment);\r
1502   GetActualPlacement(editTagsDialog, &wpTags);\r
1503   GetActualPlacement(gameListDialog, &wpGameList);\r
1504   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1505   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1506   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1507 }\r
1508 \r
1509 void\r
1510 PrintCommPortSettings(FILE *f, char *name)\r
1511 { // wrapper to shield back-end from DCB\r
1512       PrintCommSettings(f, name, &dcb);\r
1513 }\r
1514 \r
1515 int\r
1516 MySearchPath(char *installDir, char *name, char *fullname)\r
1517 {\r
1518   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1519   if(name[0]== '%') {\r
1520     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1521     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1522       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1523       *strchr(buf, '%') = 0;\r
1524       strcat(fullname, getenv(buf));\r
1525       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1526     }\r
1527     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1528     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1529     return (int) strlen(fullname);\r
1530   }\r
1531   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1532 }\r
1533 \r
1534 int\r
1535 MyGetFullPathName(char *name, char *fullname)\r
1536 {\r
1537   char *dummy;\r
1538   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1539 }\r
1540 \r
1541 int\r
1542 MainWindowUp()\r
1543 { // [HGM] args: allows testing if main window is realized from back-end\r
1544   return hwndMain != NULL;\r
1545 }\r
1546 \r
1547 void\r
1548 PopUpStartupDialog()\r
1549 {\r
1550     FARPROC lpProc;\r
1551     \r
1552     LoadLanguageFile(appData.language);\r
1553     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1554     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1555     FreeProcInstance(lpProc);\r
1556 }\r
1557 \r
1558 /*---------------------------------------------------------------------------*\\r
1559  *\r
1560  * GDI board drawing routines\r
1561  *\r
1562 \*---------------------------------------------------------------------------*/\r
1563 \r
1564 /* [AS] Draw square using background texture */\r
1565 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1566 {\r
1567     XFORM   x;\r
1568 \r
1569     if( mode == 0 ) {\r
1570         return; /* Should never happen! */\r
1571     }\r
1572 \r
1573     SetGraphicsMode( dst, GM_ADVANCED );\r
1574 \r
1575     switch( mode ) {\r
1576     case 1:\r
1577         /* Identity */\r
1578         break;\r
1579     case 2:\r
1580         /* X reflection */\r
1581         x.eM11 = -1.0;\r
1582         x.eM12 = 0;\r
1583         x.eM21 = 0;\r
1584         x.eM22 = 1.0;\r
1585         x.eDx = (FLOAT) dw + dx - 1;\r
1586         x.eDy = 0;\r
1587         dx = 0;\r
1588         SetWorldTransform( dst, &x );\r
1589         break;\r
1590     case 3:\r
1591         /* Y reflection */\r
1592         x.eM11 = 1.0;\r
1593         x.eM12 = 0;\r
1594         x.eM21 = 0;\r
1595         x.eM22 = -1.0;\r
1596         x.eDx = 0;\r
1597         x.eDy = (FLOAT) dh + dy - 1;\r
1598         dy = 0;\r
1599         SetWorldTransform( dst, &x );\r
1600         break;\r
1601     case 4:\r
1602         /* X/Y flip */\r
1603         x.eM11 = 0;\r
1604         x.eM12 = 1.0;\r
1605         x.eM21 = 1.0;\r
1606         x.eM22 = 0;\r
1607         x.eDx = (FLOAT) dx;\r
1608         x.eDy = (FLOAT) dy;\r
1609         dx = 0;\r
1610         dy = 0;\r
1611         SetWorldTransform( dst, &x );\r
1612         break;\r
1613     }\r
1614 \r
1615     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1616 \r
1617     x.eM11 = 1.0;\r
1618     x.eM12 = 0;\r
1619     x.eM21 = 0;\r
1620     x.eM22 = 1.0;\r
1621     x.eDx = 0;\r
1622     x.eDy = 0;\r
1623     SetWorldTransform( dst, &x );\r
1624 \r
1625     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1626 }\r
1627 \r
1628 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1629 enum {\r
1630     PM_WP = (int) WhitePawn, \r
1631     PM_WN = (int) WhiteKnight, \r
1632     PM_WB = (int) WhiteBishop, \r
1633     PM_WR = (int) WhiteRook, \r
1634     PM_WQ = (int) WhiteQueen, \r
1635     PM_WF = (int) WhiteFerz, \r
1636     PM_WW = (int) WhiteWazir, \r
1637     PM_WE = (int) WhiteAlfil, \r
1638     PM_WM = (int) WhiteMan, \r
1639     PM_WO = (int) WhiteCannon, \r
1640     PM_WU = (int) WhiteUnicorn, \r
1641     PM_WH = (int) WhiteNightrider, \r
1642     PM_WA = (int) WhiteAngel, \r
1643     PM_WC = (int) WhiteMarshall, \r
1644     PM_WAB = (int) WhiteCardinal, \r
1645     PM_WD = (int) WhiteDragon, \r
1646     PM_WL = (int) WhiteLance, \r
1647     PM_WS = (int) WhiteCobra, \r
1648     PM_WV = (int) WhiteFalcon, \r
1649     PM_WSG = (int) WhiteSilver, \r
1650     PM_WG = (int) WhiteGrasshopper, \r
1651     PM_WK = (int) WhiteKing,\r
1652     PM_BP = (int) BlackPawn, \r
1653     PM_BN = (int) BlackKnight, \r
1654     PM_BB = (int) BlackBishop, \r
1655     PM_BR = (int) BlackRook, \r
1656     PM_BQ = (int) BlackQueen, \r
1657     PM_BF = (int) BlackFerz, \r
1658     PM_BW = (int) BlackWazir, \r
1659     PM_BE = (int) BlackAlfil, \r
1660     PM_BM = (int) BlackMan,\r
1661     PM_BO = (int) BlackCannon, \r
1662     PM_BU = (int) BlackUnicorn, \r
1663     PM_BH = (int) BlackNightrider, \r
1664     PM_BA = (int) BlackAngel, \r
1665     PM_BC = (int) BlackMarshall, \r
1666     PM_BG = (int) BlackGrasshopper, \r
1667     PM_BAB = (int) BlackCardinal,\r
1668     PM_BD = (int) BlackDragon,\r
1669     PM_BL = (int) BlackLance,\r
1670     PM_BS = (int) BlackCobra,\r
1671     PM_BV = (int) BlackFalcon,\r
1672     PM_BSG = (int) BlackSilver,\r
1673     PM_BK = (int) BlackKing\r
1674 };\r
1675 \r
1676 static HFONT hPieceFont = NULL;\r
1677 static HBITMAP hPieceMask[(int) EmptySquare];\r
1678 static HBITMAP hPieceFace[(int) EmptySquare];\r
1679 static int fontBitmapSquareSize = 0;\r
1680 static char pieceToFontChar[(int) EmptySquare] =\r
1681                               { 'p', 'n', 'b', 'r', 'q', \r
1682                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1683                       'k', 'o', 'm', 'v', 't', 'w', \r
1684                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1685                                                               'l' };\r
1686 \r
1687 extern BOOL SetCharTable( char *table, const char * map );\r
1688 /* [HGM] moved to backend.c */\r
1689 \r
1690 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1691 {\r
1692     HBRUSH hbrush;\r
1693     BYTE r1 = GetRValue( color );\r
1694     BYTE g1 = GetGValue( color );\r
1695     BYTE b1 = GetBValue( color );\r
1696     BYTE r2 = r1 / 2;\r
1697     BYTE g2 = g1 / 2;\r
1698     BYTE b2 = b1 / 2;\r
1699     RECT rc;\r
1700 \r
1701     /* Create a uniform background first */\r
1702     hbrush = CreateSolidBrush( color );\r
1703     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1704     FillRect( hdc, &rc, hbrush );\r
1705     DeleteObject( hbrush );\r
1706     \r
1707     if( mode == 1 ) {\r
1708         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1709         int steps = squareSize / 2;\r
1710         int i;\r
1711 \r
1712         for( i=0; i<steps; i++ ) {\r
1713             BYTE r = r1 - (r1-r2) * i / steps;\r
1714             BYTE g = g1 - (g1-g2) * i / steps;\r
1715             BYTE b = b1 - (b1-b2) * i / steps;\r
1716 \r
1717             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1718             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1719             FillRect( hdc, &rc, hbrush );\r
1720             DeleteObject(hbrush);\r
1721         }\r
1722     }\r
1723     else if( mode == 2 ) {\r
1724         /* Diagonal gradient, good more or less for every piece */\r
1725         POINT triangle[3];\r
1726         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1727         HBRUSH hbrush_old;\r
1728         int steps = squareSize;\r
1729         int i;\r
1730 \r
1731         triangle[0].x = squareSize - steps;\r
1732         triangle[0].y = squareSize;\r
1733         triangle[1].x = squareSize;\r
1734         triangle[1].y = squareSize;\r
1735         triangle[2].x = squareSize;\r
1736         triangle[2].y = squareSize - steps;\r
1737 \r
1738         for( i=0; i<steps; i++ ) {\r
1739             BYTE r = r1 - (r1-r2) * i / steps;\r
1740             BYTE g = g1 - (g1-g2) * i / steps;\r
1741             BYTE b = b1 - (b1-b2) * i / steps;\r
1742 \r
1743             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1744             hbrush_old = SelectObject( hdc, hbrush );\r
1745             Polygon( hdc, triangle, 3 );\r
1746             SelectObject( hdc, hbrush_old );\r
1747             DeleteObject(hbrush);\r
1748             triangle[0].x++;\r
1749             triangle[2].y++;\r
1750         }\r
1751 \r
1752         SelectObject( hdc, hpen );\r
1753     }\r
1754 }\r
1755 \r
1756 /*\r
1757     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1758     seems to work ok. The main problem here is to find the "inside" of a chess\r
1759     piece: follow the steps as explained below.\r
1760 */\r
1761 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1762 {\r
1763     HBITMAP hbm;\r
1764     HBITMAP hbm_old;\r
1765     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1766     RECT rc;\r
1767     SIZE sz;\r
1768     POINT pt;\r
1769     int backColor = whitePieceColor; \r
1770     int foreColor = blackPieceColor;\r
1771     \r
1772     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1773         backColor = appData.fontBackColorWhite;\r
1774         foreColor = appData.fontForeColorWhite;\r
1775     }\r
1776     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1777         backColor = appData.fontBackColorBlack;\r
1778         foreColor = appData.fontForeColorBlack;\r
1779     }\r
1780 \r
1781     /* Mask */\r
1782     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1783 \r
1784     hbm_old = SelectObject( hdc, hbm );\r
1785 \r
1786     rc.left = 0;\r
1787     rc.top = 0;\r
1788     rc.right = squareSize;\r
1789     rc.bottom = squareSize;\r
1790 \r
1791     /* Step 1: background is now black */\r
1792     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1793 \r
1794     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1795 \r
1796     pt.x = (squareSize - sz.cx) / 2;\r
1797     pt.y = (squareSize - sz.cy) / 2;\r
1798 \r
1799     SetBkMode( hdc, TRANSPARENT );\r
1800     SetTextColor( hdc, chroma );\r
1801     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1802     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1803 \r
1804     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1805     /* Step 3: the area outside the piece is filled with white */\r
1806 //    FloodFill( hdc, 0, 0, chroma );\r
1807     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1808     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1809     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1810     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1811     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1812     /* \r
1813         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1814         but if the start point is not inside the piece we're lost!\r
1815         There should be a better way to do this... if we could create a region or path\r
1816         from the fill operation we would be fine for example.\r
1817     */\r
1818 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1819     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1820 \r
1821     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1822         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1823         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1824 \r
1825         SelectObject( dc2, bm2 );\r
1826         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1827         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1828         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1829         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1830         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1831 \r
1832         DeleteDC( dc2 );\r
1833         DeleteObject( bm2 );\r
1834     }\r
1835 \r
1836     SetTextColor( hdc, 0 );\r
1837     /* \r
1838         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1839         draw the piece again in black for safety.\r
1840     */\r
1841     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1842 \r
1843     SelectObject( hdc, hbm_old );\r
1844 \r
1845     if( hPieceMask[index] != NULL ) {\r
1846         DeleteObject( hPieceMask[index] );\r
1847     }\r
1848 \r
1849     hPieceMask[index] = hbm;\r
1850 \r
1851     /* Face */\r
1852     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1853 \r
1854     SelectObject( hdc, hbm );\r
1855 \r
1856     {\r
1857         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1858         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1859         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1860 \r
1861         SelectObject( dc1, hPieceMask[index] );\r
1862         SelectObject( dc2, bm2 );\r
1863         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1864         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1865         \r
1866         /* \r
1867             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1868             the piece background and deletes (makes transparent) the rest.\r
1869             Thanks to that mask, we are free to paint the background with the greates\r
1870             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1871             We use this, to make gradients and give the pieces a "roundish" look.\r
1872         */\r
1873         SetPieceBackground( hdc, backColor, 2 );\r
1874         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1875 \r
1876         DeleteDC( dc2 );\r
1877         DeleteDC( dc1 );\r
1878         DeleteObject( bm2 );\r
1879     }\r
1880 \r
1881     SetTextColor( hdc, foreColor );\r
1882     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1883 \r
1884     SelectObject( hdc, hbm_old );\r
1885 \r
1886     if( hPieceFace[index] != NULL ) {\r
1887         DeleteObject( hPieceFace[index] );\r
1888     }\r
1889 \r
1890     hPieceFace[index] = hbm;\r
1891 }\r
1892 \r
1893 static int TranslatePieceToFontPiece( int piece )\r
1894 {\r
1895     switch( piece ) {\r
1896     case BlackPawn:\r
1897         return PM_BP;\r
1898     case BlackKnight:\r
1899         return PM_BN;\r
1900     case BlackBishop:\r
1901         return PM_BB;\r
1902     case BlackRook:\r
1903         return PM_BR;\r
1904     case BlackQueen:\r
1905         return PM_BQ;\r
1906     case BlackKing:\r
1907         return PM_BK;\r
1908     case WhitePawn:\r
1909         return PM_WP;\r
1910     case WhiteKnight:\r
1911         return PM_WN;\r
1912     case WhiteBishop:\r
1913         return PM_WB;\r
1914     case WhiteRook:\r
1915         return PM_WR;\r
1916     case WhiteQueen:\r
1917         return PM_WQ;\r
1918     case WhiteKing:\r
1919         return PM_WK;\r
1920 \r
1921     case BlackAngel:\r
1922         return PM_BA;\r
1923     case BlackMarshall:\r
1924         return PM_BC;\r
1925     case BlackFerz:\r
1926         return PM_BF;\r
1927     case BlackNightrider:\r
1928         return PM_BH;\r
1929     case BlackAlfil:\r
1930         return PM_BE;\r
1931     case BlackWazir:\r
1932         return PM_BW;\r
1933     case BlackUnicorn:\r
1934         return PM_BU;\r
1935     case BlackCannon:\r
1936         return PM_BO;\r
1937     case BlackGrasshopper:\r
1938         return PM_BG;\r
1939     case BlackMan:\r
1940         return PM_BM;\r
1941     case BlackSilver:\r
1942         return PM_BSG;\r
1943     case BlackLance:\r
1944         return PM_BL;\r
1945     case BlackFalcon:\r
1946         return PM_BV;\r
1947     case BlackCobra:\r
1948         return PM_BS;\r
1949     case BlackCardinal:\r
1950         return PM_BAB;\r
1951     case BlackDragon:\r
1952         return PM_BD;\r
1953 \r
1954     case WhiteAngel:\r
1955         return PM_WA;\r
1956     case WhiteMarshall:\r
1957         return PM_WC;\r
1958     case WhiteFerz:\r
1959         return PM_WF;\r
1960     case WhiteNightrider:\r
1961         return PM_WH;\r
1962     case WhiteAlfil:\r
1963         return PM_WE;\r
1964     case WhiteWazir:\r
1965         return PM_WW;\r
1966     case WhiteUnicorn:\r
1967         return PM_WU;\r
1968     case WhiteCannon:\r
1969         return PM_WO;\r
1970     case WhiteGrasshopper:\r
1971         return PM_WG;\r
1972     case WhiteMan:\r
1973         return PM_WM;\r
1974     case WhiteSilver:\r
1975         return PM_WSG;\r
1976     case WhiteLance:\r
1977         return PM_WL;\r
1978     case WhiteFalcon:\r
1979         return PM_WV;\r
1980     case WhiteCobra:\r
1981         return PM_WS;\r
1982     case WhiteCardinal:\r
1983         return PM_WAB;\r
1984     case WhiteDragon:\r
1985         return PM_WD;\r
1986     }\r
1987 \r
1988     return 0;\r
1989 }\r
1990 \r
1991 void CreatePiecesFromFont()\r
1992 {\r
1993     LOGFONT lf;\r
1994     HDC hdc_window = NULL;\r
1995     HDC hdc = NULL;\r
1996     HFONT hfont_old;\r
1997     int fontHeight;\r
1998     int i;\r
1999 \r
2000     if( fontBitmapSquareSize < 0 ) {\r
2001         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2002         return;\r
2003     }\r
2004 \r
2005     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2006             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2007         fontBitmapSquareSize = -1;\r
2008         return;\r
2009     }\r
2010 \r
2011     if( fontBitmapSquareSize != squareSize ) {\r
2012         hdc_window = GetDC( hwndMain );\r
2013         hdc = CreateCompatibleDC( hdc_window );\r
2014 \r
2015         if( hPieceFont != NULL ) {\r
2016             DeleteObject( hPieceFont );\r
2017         }\r
2018         else {\r
2019             for( i=0; i<=(int)BlackKing; i++ ) {\r
2020                 hPieceMask[i] = NULL;\r
2021                 hPieceFace[i] = NULL;\r
2022             }\r
2023         }\r
2024 \r
2025         fontHeight = 75;\r
2026 \r
2027         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2028             fontHeight = appData.fontPieceSize;\r
2029         }\r
2030 \r
2031         fontHeight = (fontHeight * squareSize) / 100;\r
2032 \r
2033         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2034         lf.lfWidth = 0;\r
2035         lf.lfEscapement = 0;\r
2036         lf.lfOrientation = 0;\r
2037         lf.lfWeight = FW_NORMAL;\r
2038         lf.lfItalic = 0;\r
2039         lf.lfUnderline = 0;\r
2040         lf.lfStrikeOut = 0;\r
2041         lf.lfCharSet = DEFAULT_CHARSET;\r
2042         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2043         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2044         lf.lfQuality = PROOF_QUALITY;\r
2045         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2046         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2047         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2048 \r
2049         hPieceFont = CreateFontIndirect( &lf );\r
2050 \r
2051         if( hPieceFont == NULL ) {\r
2052             fontBitmapSquareSize = -2;\r
2053         }\r
2054         else {\r
2055             /* Setup font-to-piece character table */\r
2056             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2057                 /* No (or wrong) global settings, try to detect the font */\r
2058                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2059                     /* Alpha */\r
2060                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2061                 }\r
2062                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2063                     /* DiagramTT* family */\r
2064                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2065                 }\r
2066                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2067                     /* Fairy symbols */\r
2068                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2069                 }\r
2070                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2071                     /* Good Companion (Some characters get warped as literal :-( */\r
2072                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2073                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2074                     SetCharTable(pieceToFontChar, s);\r
2075                 }\r
2076                 else {\r
2077                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2078                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2079                 }\r
2080             }\r
2081 \r
2082             /* Create bitmaps */\r
2083             hfont_old = SelectObject( hdc, hPieceFont );\r
2084             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2085                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2086                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2087 \r
2088             SelectObject( hdc, hfont_old );\r
2089 \r
2090             fontBitmapSquareSize = squareSize;\r
2091         }\r
2092     }\r
2093 \r
2094     if( hdc != NULL ) {\r
2095         DeleteDC( hdc );\r
2096     }\r
2097 \r
2098     if( hdc_window != NULL ) {\r
2099         ReleaseDC( hwndMain, hdc_window );\r
2100     }\r
2101 }\r
2102 \r
2103 HBITMAP\r
2104 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2105 {\r
2106   char name[128];\r
2107 \r
2108     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2109   if (gameInfo.event &&\r
2110       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2111       strcmp(name, "k80s") == 0) {\r
2112     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2113   }\r
2114   return LoadBitmap(hinst, name);\r
2115 }\r
2116 \r
2117 \r
2118 /* Insert a color into the program's logical palette\r
2119    structure.  This code assumes the given color is\r
2120    the result of the RGB or PALETTERGB macro, and it\r
2121    knows how those macros work (which is documented).\r
2122 */\r
2123 VOID\r
2124 InsertInPalette(COLORREF color)\r
2125 {\r
2126   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2127 \r
2128   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2129     DisplayFatalError(_("Too many colors"), 0, 1);\r
2130     pLogPal->palNumEntries--;\r
2131     return;\r
2132   }\r
2133 \r
2134   pe->peFlags = (char) 0;\r
2135   pe->peRed = (char) (0xFF & color);\r
2136   pe->peGreen = (char) (0xFF & (color >> 8));\r
2137   pe->peBlue = (char) (0xFF & (color >> 16));\r
2138   return;\r
2139 }\r
2140 \r
2141 \r
2142 VOID\r
2143 InitDrawingColors()\r
2144 {\r
2145   if (pLogPal == NULL) {\r
2146     /* Allocate enough memory for a logical palette with\r
2147      * PALETTESIZE entries and set the size and version fields\r
2148      * of the logical palette structure.\r
2149      */\r
2150     pLogPal = (NPLOGPALETTE)\r
2151       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2152                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2153     pLogPal->palVersion    = 0x300;\r
2154   }\r
2155   pLogPal->palNumEntries = 0;\r
2156 \r
2157   InsertInPalette(lightSquareColor);\r
2158   InsertInPalette(darkSquareColor);\r
2159   InsertInPalette(whitePieceColor);\r
2160   InsertInPalette(blackPieceColor);\r
2161   InsertInPalette(highlightSquareColor);\r
2162   InsertInPalette(premoveHighlightColor);\r
2163 \r
2164   /*  create a logical color palette according the information\r
2165    *  in the LOGPALETTE structure.\r
2166    */\r
2167   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2168 \r
2169   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2170   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2171   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2172   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2173   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2174   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2175   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2176   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2177   /* [AS] Force rendering of the font-based pieces */\r
2178   if( fontBitmapSquareSize > 0 ) {\r
2179     fontBitmapSquareSize = 0;\r
2180   }\r
2181 }\r
2182 \r
2183 \r
2184 int\r
2185 BoardWidth(int boardSize, int n)\r
2186 { /* [HGM] argument n added to allow different width and height */\r
2187   int lineGap = sizeInfo[boardSize].lineGap;\r
2188 \r
2189   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2190       lineGap = appData.overrideLineGap;\r
2191   }\r
2192 \r
2193   return (n + 1) * lineGap +\r
2194           n * sizeInfo[boardSize].squareSize;\r
2195 }\r
2196 \r
2197 /* Respond to board resize by dragging edge */\r
2198 VOID\r
2199 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2200 {\r
2201   BoardSize newSize = NUM_SIZES - 1;\r
2202   static int recurse = 0;\r
2203   if (IsIconic(hwndMain)) return;\r
2204   if (recurse > 0) return;\r
2205   recurse++;\r
2206   while (newSize > 0) {\r
2207         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2208         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2209            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2210     newSize--;\r
2211   } \r
2212   boardSize = newSize;\r
2213   InitDrawingSizes(boardSize, flags);\r
2214   recurse--;\r
2215 }\r
2216 \r
2217 \r
2218 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2219 \r
2220 VOID\r
2221 InitDrawingSizes(BoardSize boardSize, int flags)\r
2222 {\r
2223   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2224   ChessSquare piece;\r
2225   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2226   HDC hdc;\r
2227   SIZE clockSize, messageSize;\r
2228   HFONT oldFont;\r
2229   char buf[MSG_SIZ];\r
2230   char *str;\r
2231   HMENU hmenu = GetMenu(hwndMain);\r
2232   RECT crect, wrect, oldRect;\r
2233   int offby;\r
2234   LOGBRUSH logbrush;\r
2235 \r
2236   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2237   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2238 \r
2239   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2240   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2241 \r
2242   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2243   oldRect.top = wpMain.y;\r
2244   oldRect.right = wpMain.x + wpMain.width;\r
2245   oldRect.bottom = wpMain.y + wpMain.height;\r
2246 \r
2247   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2248   smallLayout = sizeInfo[boardSize].smallLayout;\r
2249   squareSize = sizeInfo[boardSize].squareSize;\r
2250   lineGap = sizeInfo[boardSize].lineGap;\r
2251   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2252 \r
2253   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2254       lineGap = appData.overrideLineGap;\r
2255   }\r
2256 \r
2257   if (tinyLayout != oldTinyLayout) {\r
2258     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2259     if (tinyLayout) {\r
2260       style &= ~WS_SYSMENU;\r
2261       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2262                  "&Minimize\tCtrl+F4");\r
2263     } else {\r
2264       style |= WS_SYSMENU;\r
2265       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2266     }\r
2267     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2268 \r
2269     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2270       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2271         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2272     }\r
2273     DrawMenuBar(hwndMain);\r
2274   }\r
2275 \r
2276   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2277   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2278 \r
2279   /* Get text area sizes */\r
2280   hdc = GetDC(hwndMain);\r
2281   if (appData.clockMode) {\r
2282     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2283   } else {\r
2284     snprintf(buf, MSG_SIZ, _("White"));\r
2285   }\r
2286   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2287   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2288   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2289   str = _("We only care about the height here");\r
2290   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2291   SelectObject(hdc, oldFont);\r
2292   ReleaseDC(hwndMain, hdc);\r
2293 \r
2294   /* Compute where everything goes */\r
2295   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2296         /* [HGM] logo: if either logo is on, reserve space for it */\r
2297         logoHeight =  2*clockSize.cy;\r
2298         leftLogoRect.left   = OUTER_MARGIN;\r
2299         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2300         leftLogoRect.top    = OUTER_MARGIN;\r
2301         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2302 \r
2303         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2304         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2305         rightLogoRect.top    = OUTER_MARGIN;\r
2306         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2307 \r
2308 \r
2309     whiteRect.left = leftLogoRect.right;\r
2310     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2311     whiteRect.top = OUTER_MARGIN;\r
2312     whiteRect.bottom = whiteRect.top + logoHeight;\r
2313 \r
2314     blackRect.right = rightLogoRect.left;\r
2315     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2316     blackRect.top = whiteRect.top;\r
2317     blackRect.bottom = whiteRect.bottom;\r
2318   } else {\r
2319     whiteRect.left = OUTER_MARGIN;\r
2320     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2321     whiteRect.top = OUTER_MARGIN;\r
2322     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2323 \r
2324     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2325     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2326     blackRect.top = whiteRect.top;\r
2327     blackRect.bottom = whiteRect.bottom;\r
2328 \r
2329     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2330   }\r
2331 \r
2332   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2333   if (appData.showButtonBar) {\r
2334     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2335       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2336   } else {\r
2337     messageRect.right = OUTER_MARGIN + boardWidth;\r
2338   }\r
2339   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2340   messageRect.bottom = messageRect.top + messageSize.cy;\r
2341 \r
2342   boardRect.left = OUTER_MARGIN;\r
2343   boardRect.right = boardRect.left + boardWidth;\r
2344   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2345   boardRect.bottom = boardRect.top + boardHeight;\r
2346 \r
2347   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2348   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2349   oldBoardSize = boardSize;\r
2350   oldTinyLayout = tinyLayout;\r
2351   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2352   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2353     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2354   winW *= 1 + twoBoards;\r
2355   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2356   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2357   wpMain.height = winH; //       without disturbing window attachments\r
2358   GetWindowRect(hwndMain, &wrect);\r
2359   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2360                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2361 \r
2362   // [HGM] placement: let attached windows follow size change.\r
2363   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2364   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2365   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2366   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2367   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2368 \r
2369   /* compensate if menu bar wrapped */\r
2370   GetClientRect(hwndMain, &crect);\r
2371   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2372   wpMain.height += offby;\r
2373   switch (flags) {\r
2374   case WMSZ_TOPLEFT:\r
2375     SetWindowPos(hwndMain, NULL, \r
2376                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2377                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2378     break;\r
2379 \r
2380   case WMSZ_TOPRIGHT:\r
2381   case WMSZ_TOP:\r
2382     SetWindowPos(hwndMain, NULL, \r
2383                  wrect.left, wrect.bottom - wpMain.height, \r
2384                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2385     break;\r
2386 \r
2387   case WMSZ_BOTTOMLEFT:\r
2388   case WMSZ_LEFT:\r
2389     SetWindowPos(hwndMain, NULL, \r
2390                  wrect.right - wpMain.width, wrect.top, \r
2391                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2392     break;\r
2393 \r
2394   case WMSZ_BOTTOMRIGHT:\r
2395   case WMSZ_BOTTOM:\r
2396   case WMSZ_RIGHT:\r
2397   default:\r
2398     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2399                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2400     break;\r
2401   }\r
2402 \r
2403   hwndPause = NULL;\r
2404   for (i = 0; i < N_BUTTONS; i++) {\r
2405     if (buttonDesc[i].hwnd != NULL) {\r
2406       DestroyWindow(buttonDesc[i].hwnd);\r
2407       buttonDesc[i].hwnd = NULL;\r
2408     }\r
2409     if (appData.showButtonBar) {\r
2410       buttonDesc[i].hwnd =\r
2411         CreateWindow("BUTTON", buttonDesc[i].label,\r
2412                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2413                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2414                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2415                      (HMENU) buttonDesc[i].id,\r
2416                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2417       if (tinyLayout) {\r
2418         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2419                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2420                     MAKELPARAM(FALSE, 0));\r
2421       }\r
2422       if (buttonDesc[i].id == IDM_Pause)\r
2423         hwndPause = buttonDesc[i].hwnd;\r
2424       buttonDesc[i].wndproc = (WNDPROC)\r
2425         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2426     }\r
2427   }\r
2428   if (gridPen != NULL) DeleteObject(gridPen);\r
2429   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2430   if (premovePen != NULL) DeleteObject(premovePen);\r
2431   if (lineGap != 0) {\r
2432     logbrush.lbStyle = BS_SOLID;\r
2433     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2434     gridPen =\r
2435       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2436                    lineGap, &logbrush, 0, NULL);\r
2437     logbrush.lbColor = highlightSquareColor;\r
2438     highlightPen =\r
2439       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2440                    lineGap, &logbrush, 0, NULL);\r
2441 \r
2442     logbrush.lbColor = premoveHighlightColor; \r
2443     premovePen =\r
2444       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2445                    lineGap, &logbrush, 0, NULL);\r
2446 \r
2447     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2448     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2449       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2450       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2451         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2452       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2453         BOARD_WIDTH * (squareSize + lineGap);\r
2454       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2455     }\r
2456     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2457       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2458       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2459         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2460         lineGap / 2 + (i * (squareSize + lineGap));\r
2461       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2462         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2463       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2464     }\r
2465   }\r
2466 \r
2467   /* [HGM] Licensing requirement */\r
2468 #ifdef GOTHIC\r
2469   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2470 #endif\r
2471 #ifdef FALCON\r
2472   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2473 #endif\r
2474   GothicPopUp( "", VariantNormal);\r
2475 \r
2476 \r
2477 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2478 \r
2479   /* Load piece bitmaps for this board size */\r
2480   for (i=0; i<=2; i++) {\r
2481     for (piece = WhitePawn;\r
2482          (int) piece < (int) BlackPawn;\r
2483          piece = (ChessSquare) ((int) piece + 1)) {\r
2484       if (pieceBitmap[i][piece] != NULL)\r
2485         DeleteObject(pieceBitmap[i][piece]);\r
2486     }\r
2487   }\r
2488 \r
2489   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2490   // Orthodox Chess pieces\r
2491   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2492   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2493   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2494   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2495   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2496   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2497   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2498   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2499   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2500   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2501   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2502   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2503   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2504   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2505   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2506   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2507     // in Shogi, Hijack the unused Queen for Lance\r
2508     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2509     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2510     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2511   } else {\r
2512     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2513     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2514     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2515   }\r
2516 \r
2517   if(squareSize <= 72 && squareSize >= 33) { \r
2518     /* A & C are available in most sizes now */\r
2519     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2520       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2521       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2522       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2523       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2524       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2525       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2526       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2527       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2528       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2529       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2530       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2531       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2532     } else { // Smirf-like\r
2533       if(gameInfo.variant == VariantSChess) {\r
2534         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2535         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2536         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2537       } else {\r
2538         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2539         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2540         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2541       }\r
2542     }\r
2543     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2544       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2545       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2546       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2547     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2548       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2549       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2550       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2551     } else { // WinBoard standard\r
2552       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2553       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2554       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2555     }\r
2556   }\r
2557 \r
2558 \r
2559   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2560     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2561     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2562     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2563     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2564     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2565     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2566     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2567     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2568     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2569     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2570     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2571     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2572     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2573     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2574     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2575     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2576     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2577     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2578     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2579     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2580     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2581     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2582     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2583     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2584     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2585     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2586     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2587     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2588     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2589     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2590 \r
2591     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2592       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2593       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2594       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2595       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2596       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2597       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2598       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2599       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2600       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2601       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2602       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2603       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2604     } else {\r
2605       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2606       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2607       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2608       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2609       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2610       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2611       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2612       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2613       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2614       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2615       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2616       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2617     }\r
2618 \r
2619   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2620     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2621     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2622     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2623     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2624     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2625     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2626     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2627     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2628     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2629     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2630     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2631     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2632     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2633     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2634   }\r
2635 \r
2636 \r
2637   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2638   /* special Shogi support in this size */\r
2639   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2640       for (piece = WhitePawn;\r
2641            (int) piece < (int) BlackPawn;\r
2642            piece = (ChessSquare) ((int) piece + 1)) {\r
2643         if (pieceBitmap[i][piece] != NULL)\r
2644           DeleteObject(pieceBitmap[i][piece]);\r
2645       }\r
2646     }\r
2647   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2648   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2649   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2650   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2651   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2652   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2653   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2654   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2655   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2656   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2657   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2658   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2659   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2660   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2661   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2662   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2663   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2664   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2665   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2666   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2667   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2668   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2669   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2670   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2671   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2672   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2673   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2674   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2675   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2676   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2677   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2678   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2679   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2680   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2681   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2682   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2683   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2684   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2685   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2686   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2687   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2688   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2689   minorSize = 0;\r
2690   }\r
2691 }\r
2692 \r
2693 HBITMAP\r
2694 PieceBitmap(ChessSquare p, int kind)\r
2695 {\r
2696   if ((int) p >= (int) BlackPawn)\r
2697     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2698 \r
2699   return pieceBitmap[kind][(int) p];\r
2700 }\r
2701 \r
2702 /***************************************************************/\r
2703 \r
2704 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2705 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2706 /*\r
2707 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2708 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2709 */\r
2710 \r
2711 VOID\r
2712 SquareToPos(int row, int column, int * x, int * y)\r
2713 {\r
2714   if (flipView) {\r
2715     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2716     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2717   } else {\r
2718     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2719     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2720   }\r
2721 }\r
2722 \r
2723 VOID\r
2724 DrawCoordsOnDC(HDC hdc)\r
2725 {\r
2726   static char files[] = "0123456789012345678901221098765432109876543210";\r
2727   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2728   char str[2] = { NULLCHAR, NULLCHAR };\r
2729   int oldMode, oldAlign, x, y, start, i;\r
2730   HFONT oldFont;\r
2731   HBRUSH oldBrush;\r
2732 \r
2733   if (!appData.showCoords)\r
2734     return;\r
2735 \r
2736   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2737 \r
2738   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2739   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2740   oldAlign = GetTextAlign(hdc);\r
2741   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2742 \r
2743   y = boardRect.top + lineGap;\r
2744   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2745 \r
2746   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2747   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2748     str[0] = files[start + i];\r
2749     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2750     y += squareSize + lineGap;\r
2751   }\r
2752 \r
2753   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2754 \r
2755   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2756   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2757     str[0] = ranks[start + i];\r
2758     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2759     x += squareSize + lineGap;\r
2760   }    \r
2761 \r
2762   SelectObject(hdc, oldBrush);\r
2763   SetBkMode(hdc, oldMode);\r
2764   SetTextAlign(hdc, oldAlign);\r
2765   SelectObject(hdc, oldFont);\r
2766 }\r
2767 \r
2768 VOID\r
2769 DrawGridOnDC(HDC hdc)\r
2770 {\r
2771   HPEN oldPen;\r
2772  \r
2773   if (lineGap != 0) {\r
2774     oldPen = SelectObject(hdc, gridPen);\r
2775     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2776     SelectObject(hdc, oldPen);\r
2777   }\r
2778 }\r
2779 \r
2780 #define HIGHLIGHT_PEN 0\r
2781 #define PREMOVE_PEN   1\r
2782 \r
2783 VOID\r
2784 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2785 {\r
2786   int x1, y1;\r
2787   HPEN oldPen, hPen;\r
2788   if (lineGap == 0) return;\r
2789   if (flipView) {\r
2790     x1 = boardRect.left +\r
2791       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2792     y1 = boardRect.top +\r
2793       lineGap/2 + y * (squareSize + lineGap);\r
2794   } else {\r
2795     x1 = boardRect.left +\r
2796       lineGap/2 + x * (squareSize + lineGap);\r
2797     y1 = boardRect.top +\r
2798       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2799   }\r
2800   hPen = pen ? premovePen : highlightPen;\r
2801   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2802   MoveToEx(hdc, x1, y1, NULL);\r
2803   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2804   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2805   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2806   LineTo(hdc, x1, y1);\r
2807   SelectObject(hdc, oldPen);\r
2808 }\r
2809 \r
2810 VOID\r
2811 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2812 {\r
2813   int i;\r
2814   for (i=0; i<2; i++) {\r
2815     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2816       DrawHighlightOnDC(hdc, TRUE,\r
2817                         h->sq[i].x, h->sq[i].y,\r
2818                         pen);\r
2819   }\r
2820 }\r
2821 \r
2822 /* Note: sqcolor is used only in monoMode */\r
2823 /* Note that this code is largely duplicated in woptions.c,\r
2824    function DrawSampleSquare, so that needs to be updated too */\r
2825 VOID\r
2826 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2827 {\r
2828   HBITMAP oldBitmap;\r
2829   HBRUSH oldBrush;\r
2830   int tmpSize;\r
2831 \r
2832   if (appData.blindfold) return;\r
2833 \r
2834   /* [AS] Use font-based pieces if needed */\r
2835   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2836     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2837     CreatePiecesFromFont();\r
2838 \r
2839     if( fontBitmapSquareSize == squareSize ) {\r
2840         int index = TranslatePieceToFontPiece(piece);\r
2841 \r
2842         SelectObject( tmphdc, hPieceMask[ index ] );\r
2843 \r
2844       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2845         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2846       else\r
2847         BitBlt( hdc,\r
2848             x, y,\r
2849             squareSize, squareSize,\r
2850             tmphdc,\r
2851             0, 0,\r
2852             SRCAND );\r
2853 \r
2854         SelectObject( tmphdc, hPieceFace[ index ] );\r
2855 \r
2856       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2857         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2858       else\r
2859         BitBlt( hdc,\r
2860             x, y,\r
2861             squareSize, squareSize,\r
2862             tmphdc,\r
2863             0, 0,\r
2864             SRCPAINT );\r
2865 \r
2866         return;\r
2867     }\r
2868   }\r
2869 \r
2870   if (appData.monoMode) {\r
2871     SelectObject(tmphdc, PieceBitmap(piece, \r
2872       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2873     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2874            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2875   } else {\r
2876     tmpSize = squareSize;\r
2877     if(minorSize &&\r
2878         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2879          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2880       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2881       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2882       x += (squareSize - minorSize)>>1;\r
2883       y += squareSize - minorSize - 2;\r
2884       tmpSize = minorSize;\r
2885     }\r
2886     if (color || appData.allWhite ) {\r
2887       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2888       if( color )\r
2889               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2890       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2891       if(appData.upsideDown && color==flipView)\r
2892         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2893       else\r
2894         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2895       /* Use black for outline of white pieces */\r
2896       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2897       if(appData.upsideDown && color==flipView)\r
2898         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2899       else\r
2900         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2901     } else {\r
2902       /* Use square color for details of black pieces */\r
2903       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2904       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2905       if(appData.upsideDown && !flipView)\r
2906         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2907       else\r
2908         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2909     }\r
2910     SelectObject(hdc, oldBrush);\r
2911     SelectObject(tmphdc, oldBitmap);\r
2912   }\r
2913 }\r
2914 \r
2915 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2916 int GetBackTextureMode( int algo )\r
2917 {\r
2918     int result = BACK_TEXTURE_MODE_DISABLED;\r
2919 \r
2920     switch( algo ) \r
2921     {\r
2922         case BACK_TEXTURE_MODE_PLAIN:\r
2923             result = 1; /* Always use identity map */\r
2924             break;\r
2925         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2926             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2927             break;\r
2928     }\r
2929 \r
2930     return result;\r
2931 }\r
2932 \r
2933 /* \r
2934     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2935     to handle redraws cleanly (as random numbers would always be different).\r
2936 */\r
2937 VOID RebuildTextureSquareInfo()\r
2938 {\r
2939     BITMAP bi;\r
2940     int lite_w = 0;\r
2941     int lite_h = 0;\r
2942     int dark_w = 0;\r
2943     int dark_h = 0;\r
2944     int row;\r
2945     int col;\r
2946 \r
2947     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2948 \r
2949     if( liteBackTexture != NULL ) {\r
2950         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2951             lite_w = bi.bmWidth;\r
2952             lite_h = bi.bmHeight;\r
2953         }\r
2954     }\r
2955 \r
2956     if( darkBackTexture != NULL ) {\r
2957         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2958             dark_w = bi.bmWidth;\r
2959             dark_h = bi.bmHeight;\r
2960         }\r
2961     }\r
2962 \r
2963     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2964         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2965             if( (col + row) & 1 ) {\r
2966                 /* Lite square */\r
2967                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2968                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2969                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2970                   else\r
2971                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2972                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2973                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2974                   else\r
2975                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2976                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2977                 }\r
2978             }\r
2979             else {\r
2980                 /* Dark square */\r
2981                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2982                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2983                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2984                   else\r
2985                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2986                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2987                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2988                   else\r
2989                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2990                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2991                 }\r
2992             }\r
2993         }\r
2994     }\r
2995 }\r
2996 \r
2997 /* [AS] Arrow highlighting support */\r
2998 \r
2999 static double A_WIDTH = 5; /* Width of arrow body */\r
3000 \r
3001 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3002 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3003 \r
3004 static double Sqr( double x )\r
3005 {\r
3006     return x*x;\r
3007 }\r
3008 \r
3009 static int Round( double x )\r
3010 {\r
3011     return (int) (x + 0.5);\r
3012 }\r
3013 \r
3014 /* Draw an arrow between two points using current settings */\r
3015 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3016 {\r
3017     POINT arrow[7];\r
3018     double dx, dy, j, k, x, y;\r
3019 \r
3020     if( d_x == s_x ) {\r
3021         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3022 \r
3023         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3024         arrow[0].y = s_y;\r
3025 \r
3026         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3027         arrow[1].y = d_y - h;\r
3028 \r
3029         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3030         arrow[2].y = d_y - h;\r
3031 \r
3032         arrow[3].x = d_x;\r
3033         arrow[3].y = d_y;\r
3034 \r
3035         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3036         arrow[5].y = d_y - h;\r
3037 \r
3038         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3039         arrow[4].y = d_y - h;\r
3040 \r
3041         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3042         arrow[6].y = s_y;\r
3043     }\r
3044     else if( d_y == s_y ) {\r
3045         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3046 \r
3047         arrow[0].x = s_x;\r
3048         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3049 \r
3050         arrow[1].x = d_x - w;\r
3051         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3052 \r
3053         arrow[2].x = d_x - w;\r
3054         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3055 \r
3056         arrow[3].x = d_x;\r
3057         arrow[3].y = d_y;\r
3058 \r
3059         arrow[5].x = d_x - w;\r
3060         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3061 \r
3062         arrow[4].x = d_x - w;\r
3063         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3064 \r
3065         arrow[6].x = s_x;\r
3066         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3067     }\r
3068     else {\r
3069         /* [AS] Needed a lot of paper for this! :-) */\r
3070         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3071         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3072   \r
3073         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3074 \r
3075         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3076 \r
3077         x = s_x;\r
3078         y = s_y;\r
3079 \r
3080         arrow[0].x = Round(x - j);\r
3081         arrow[0].y = Round(y + j*dx);\r
3082 \r
3083         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3084         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3085 \r
3086         if( d_x > s_x ) {\r
3087             x = (double) d_x - k;\r
3088             y = (double) d_y - k*dy;\r
3089         }\r
3090         else {\r
3091             x = (double) d_x + k;\r
3092             y = (double) d_y + k*dy;\r
3093         }\r
3094 \r
3095         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3096 \r
3097         arrow[6].x = Round(x - j);\r
3098         arrow[6].y = Round(y + j*dx);\r
3099 \r
3100         arrow[2].x = Round(arrow[6].x + 2*j);\r
3101         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3102 \r
3103         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3104         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3105 \r
3106         arrow[4].x = d_x;\r
3107         arrow[4].y = d_y;\r
3108 \r
3109         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3110         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3111     }\r
3112 \r
3113     Polygon( hdc, arrow, 7 );\r
3114 }\r
3115 \r
3116 /* [AS] Draw an arrow between two squares */\r
3117 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3118 {\r
3119     int s_x, s_y, d_x, d_y;\r
3120     HPEN hpen;\r
3121     HPEN holdpen;\r
3122     HBRUSH hbrush;\r
3123     HBRUSH holdbrush;\r
3124     LOGBRUSH stLB;\r
3125 \r
3126     if( s_col == d_col && s_row == d_row ) {\r
3127         return;\r
3128     }\r
3129 \r
3130     /* Get source and destination points */\r
3131     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3132     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3133 \r
3134     if( d_y > s_y ) {\r
3135         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3136     }\r
3137     else if( d_y < s_y ) {\r
3138         d_y += squareSize / 2 + squareSize / 4;\r
3139     }\r
3140     else {\r
3141         d_y += squareSize / 2;\r
3142     }\r
3143 \r
3144     if( d_x > s_x ) {\r
3145         d_x += squareSize / 2 - squareSize / 4;\r
3146     }\r
3147     else if( d_x < s_x ) {\r
3148         d_x += squareSize / 2 + squareSize / 4;\r
3149     }\r
3150     else {\r
3151         d_x += squareSize / 2;\r
3152     }\r
3153 \r
3154     s_x += squareSize / 2;\r
3155     s_y += squareSize / 2;\r
3156 \r
3157     /* Adjust width */\r
3158     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3159 \r
3160     /* Draw */\r
3161     stLB.lbStyle = BS_SOLID;\r
3162     stLB.lbColor = appData.highlightArrowColor;\r
3163     stLB.lbHatch = 0;\r
3164 \r
3165     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3166     holdpen = SelectObject( hdc, hpen );\r
3167     hbrush = CreateBrushIndirect( &stLB );\r
3168     holdbrush = SelectObject( hdc, hbrush );\r
3169 \r
3170     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3171 \r
3172     SelectObject( hdc, holdpen );\r
3173     SelectObject( hdc, holdbrush );\r
3174     DeleteObject( hpen );\r
3175     DeleteObject( hbrush );\r
3176 }\r
3177 \r
3178 BOOL HasHighlightInfo()\r
3179 {\r
3180     BOOL result = FALSE;\r
3181 \r
3182     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3183         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3184     {\r
3185         result = TRUE;\r
3186     }\r
3187 \r
3188     return result;\r
3189 }\r
3190 \r
3191 BOOL IsDrawArrowEnabled()\r
3192 {\r
3193     BOOL result = FALSE;\r
3194 \r
3195     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3196         result = TRUE;\r
3197     }\r
3198 \r
3199     return result;\r
3200 }\r
3201 \r
3202 VOID DrawArrowHighlight( HDC hdc )\r
3203 {\r
3204     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3205         DrawArrowBetweenSquares( hdc,\r
3206             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3207             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3208     }\r
3209 }\r
3210 \r
3211 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3212 {\r
3213     HRGN result = NULL;\r
3214 \r
3215     if( HasHighlightInfo() ) {\r
3216         int x1, y1, x2, y2;\r
3217         int sx, sy, dx, dy;\r
3218 \r
3219         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3220         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3221 \r
3222         sx = MIN( x1, x2 );\r
3223         sy = MIN( y1, y2 );\r
3224         dx = MAX( x1, x2 ) + squareSize;\r
3225         dy = MAX( y1, y2 ) + squareSize;\r
3226 \r
3227         result = CreateRectRgn( sx, sy, dx, dy );\r
3228     }\r
3229 \r
3230     return result;\r
3231 }\r
3232 \r
3233 /*\r
3234     Warning: this function modifies the behavior of several other functions. \r
3235     \r
3236     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3237     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3238     repaint is scattered all over the place, which is not good for features such as\r
3239     "arrow highlighting" that require a full repaint of the board.\r
3240 \r
3241     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3242     user interaction, when speed is not so important) but especially to avoid errors\r
3243     in the displayed graphics.\r
3244 \r
3245     In such patched places, I always try refer to this function so there is a single\r
3246     place to maintain knowledge.\r
3247     \r
3248     To restore the original behavior, just return FALSE unconditionally.\r
3249 */\r
3250 BOOL IsFullRepaintPreferrable()\r
3251 {\r
3252     BOOL result = FALSE;\r
3253 \r
3254     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3255         /* Arrow may appear on the board */\r
3256         result = TRUE;\r
3257     }\r
3258 \r
3259     return result;\r
3260 }\r
3261 \r
3262 /* \r
3263     This function is called by DrawPosition to know whether a full repaint must\r
3264     be forced or not.\r
3265 \r
3266     Only DrawPosition may directly call this function, which makes use of \r
3267     some state information. Other function should call DrawPosition specifying \r
3268     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3269 */\r
3270 BOOL DrawPositionNeedsFullRepaint()\r
3271 {\r
3272     BOOL result = FALSE;\r
3273 \r
3274     /* \r
3275         Probably a slightly better policy would be to trigger a full repaint\r
3276         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3277         but animation is fast enough that it's difficult to notice.\r
3278     */\r
3279     if( animInfo.piece == EmptySquare ) {\r
3280         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3281             result = TRUE;\r
3282         }\r
3283     }\r
3284 \r
3285     return result;\r
3286 }\r
3287 \r
3288 VOID\r
3289 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3290 {\r
3291   int row, column, x, y, square_color, piece_color;\r
3292   ChessSquare piece;\r
3293   HBRUSH oldBrush;\r
3294   HDC texture_hdc = NULL;\r
3295 \r
3296   /* [AS] Initialize background textures if needed */\r
3297   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3298       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3299       if( backTextureSquareSize != squareSize \r
3300        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3301           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3302           backTextureSquareSize = squareSize;\r
3303           RebuildTextureSquareInfo();\r
3304       }\r
3305 \r
3306       texture_hdc = CreateCompatibleDC( hdc );\r
3307   }\r
3308 \r
3309   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3310     for (column = 0; column < BOARD_WIDTH; column++) {\r
3311   \r
3312       SquareToPos(row, column, &x, &y);\r
3313 \r
3314       piece = board[row][column];\r
3315 \r
3316       square_color = ((column + row) % 2) == 1;\r
3317       if( gameInfo.variant == VariantXiangqi ) {\r
3318           square_color = !InPalace(row, column);\r
3319           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3320           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3321       }\r
3322       piece_color = (int) piece < (int) BlackPawn;\r
3323 \r
3324 \r
3325       /* [HGM] holdings file: light square or black */\r
3326       if(column == BOARD_LEFT-2) {\r
3327             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3328                 square_color = 1;\r
3329             else {\r
3330                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3331                 continue;\r
3332             }\r
3333       } else\r
3334       if(column == BOARD_RGHT + 1 ) {\r
3335             if( row < gameInfo.holdingsSize )\r
3336                 square_color = 1;\r
3337             else {\r
3338                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3339                 continue;\r
3340             }\r
3341       }\r
3342       if(column == BOARD_LEFT-1 ) /* left align */\r
3343             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3344       else if( column == BOARD_RGHT) /* right align */\r
3345             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3346       else\r
3347       if (appData.monoMode) {\r
3348         if (piece == EmptySquare) {\r
3349           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3350                  square_color ? WHITENESS : BLACKNESS);\r
3351         } else {\r
3352           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3353         }\r
3354       } \r
3355       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3356           /* [AS] Draw the square using a texture bitmap */\r
3357           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3358           int r = row, c = column; // [HGM] do not flip board in flipView\r
3359           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3360 \r
3361           DrawTile( x, y, \r
3362               squareSize, squareSize, \r
3363               hdc, \r
3364               texture_hdc,\r
3365               backTextureSquareInfo[r][c].mode,\r
3366               backTextureSquareInfo[r][c].x,\r
3367               backTextureSquareInfo[r][c].y );\r
3368 \r
3369           SelectObject( texture_hdc, hbm );\r
3370 \r
3371           if (piece != EmptySquare) {\r
3372               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3373           }\r
3374       }\r
3375       else {\r
3376         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3377 \r
3378         oldBrush = SelectObject(hdc, brush );\r
3379         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3380         SelectObject(hdc, oldBrush);\r
3381         if (piece != EmptySquare)\r
3382           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3383       }\r
3384     }\r
3385   }\r
3386 \r
3387   if( texture_hdc != NULL ) {\r
3388     DeleteDC( texture_hdc );\r
3389   }\r
3390 }\r
3391 \r
3392 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3393 void fputDW(FILE *f, int x)\r
3394 {\r
3395         fputc(x     & 255, f);\r
3396         fputc(x>>8  & 255, f);\r
3397         fputc(x>>16 & 255, f);\r
3398         fputc(x>>24 & 255, f);\r
3399 }\r
3400 \r
3401 #define MAX_CLIPS 200   /* more than enough */\r
3402 \r
3403 VOID\r
3404 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3405 {\r
3406 //  HBITMAP bufferBitmap;\r
3407   BITMAP bi;\r
3408 //  RECT Rect;\r
3409   HDC tmphdc;\r
3410   HBITMAP hbm;\r
3411   int w = 100, h = 50;\r
3412 \r
3413   if(logo == NULL) {\r
3414     if(!logoHeight) return;\r
3415     FillRect( hdc, &logoRect, whitePieceBrush );\r
3416   }\r
3417 //  GetClientRect(hwndMain, &Rect);\r
3418 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3419 //                                      Rect.bottom-Rect.top+1);\r
3420   tmphdc = CreateCompatibleDC(hdc);\r
3421   hbm = SelectObject(tmphdc, logo);\r
3422   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3423             w = bi.bmWidth;\r
3424             h = bi.bmHeight;\r
3425   }\r
3426   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3427                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3428   SelectObject(tmphdc, hbm);\r
3429   DeleteDC(tmphdc);\r
3430 }\r
3431 \r
3432 VOID\r
3433 DisplayLogos()\r
3434 {\r
3435   if(logoHeight) {\r
3436         HDC hdc = GetDC(hwndMain);\r
3437         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3438         if(appData.autoLogo) {\r
3439           \r
3440           switch(gameMode) { // pick logos based on game mode\r
3441             case IcsObserving:\r
3442                 whiteLogo = second.programLogo; // ICS logo\r
3443                 blackLogo = second.programLogo;\r
3444             default:\r
3445                 break;\r
3446             case IcsPlayingWhite:\r
3447                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3448                 blackLogo = second.programLogo; // ICS logo\r
3449                 break;\r
3450             case IcsPlayingBlack:\r
3451                 whiteLogo = second.programLogo; // ICS logo\r
3452                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3453                 break;\r
3454             case TwoMachinesPlay:\r
3455                 if(first.twoMachinesColor[0] == 'b') {\r
3456                     whiteLogo = second.programLogo;\r
3457                     blackLogo = first.programLogo;\r
3458                 }\r
3459                 break;\r
3460             case MachinePlaysWhite:\r
3461                 blackLogo = userLogo;\r
3462                 break;\r
3463             case MachinePlaysBlack:\r
3464                 whiteLogo = userLogo;\r
3465                 blackLogo = first.programLogo;\r
3466           }\r
3467         }\r
3468         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3469         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3470         ReleaseDC(hwndMain, hdc);\r
3471   }\r
3472 }\r
3473 \r
3474 void\r
3475 UpdateLogos(int display)\r
3476 { // called after loading new engine(s), in tourney or from menu\r
3477   LoadLogo(&first, 0, FALSE);\r
3478   LoadLogo(&second, 1, appData.icsActive);\r
3479   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3480   if(display) DisplayLogos();\r
3481 }\r
3482 \r
3483 static HDC hdcSeek;\r
3484 \r
3485 // [HGM] seekgraph\r
3486 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3487 {\r
3488     POINT stPt;\r
3489     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3490     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3491     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3492     SelectObject( hdcSeek, hp );\r
3493 }\r
3494 \r
3495 // front-end wrapper for drawing functions to do rectangles\r
3496 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3497 {\r
3498     HPEN hp;\r
3499     RECT rc;\r
3500 \r
3501     if (hdcSeek == NULL) {\r
3502     hdcSeek = GetDC(hwndMain);\r
3503       if (!appData.monoMode) {\r
3504         SelectPalette(hdcSeek, hPal, FALSE);\r
3505         RealizePalette(hdcSeek);\r
3506       }\r
3507     }\r
3508     hp = SelectObject( hdcSeek, gridPen );\r
3509     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3510     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3511     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3512     SelectObject( hdcSeek, hp );\r
3513 }\r
3514 \r
3515 // front-end wrapper for putting text in graph\r
3516 void DrawSeekText(char *buf, int x, int y)\r
3517 {\r
3518         SIZE stSize;\r
3519         SetBkMode( hdcSeek, TRANSPARENT );\r
3520         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3521         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3522 }\r
3523 \r
3524 void DrawSeekDot(int x, int y, int color)\r
3525 {\r
3526         int square = color & 0x80;\r
3527         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3528                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3529         color &= 0x7F;\r
3530         if(square)\r
3531             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3532                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3533         else\r
3534             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3535                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3536             SelectObject(hdcSeek, oldBrush);\r
3537 }\r
3538 \r
3539 VOID\r
3540 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3541 {\r
3542   static Board lastReq[2], lastDrawn[2];\r
3543   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3544   static int lastDrawnFlipView = 0;\r
3545   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3546   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3547   HDC tmphdc;\r
3548   HDC hdcmem;\r
3549   HBITMAP bufferBitmap;\r
3550   HBITMAP oldBitmap;\r
3551   RECT Rect;\r
3552   HRGN clips[MAX_CLIPS];\r
3553   ChessSquare dragged_piece = EmptySquare;\r
3554   int nr = twoBoards*partnerUp;\r
3555 \r
3556   /* I'm undecided on this - this function figures out whether a full\r
3557    * repaint is necessary on its own, so there's no real reason to have the\r
3558    * caller tell it that.  I think this can safely be set to FALSE - but\r
3559    * if we trust the callers not to request full repaints unnessesarily, then\r
3560    * we could skip some clipping work.  In other words, only request a full\r
3561    * redraw when the majority of pieces have changed positions (ie. flip, \r
3562    * gamestart and similar)  --Hawk\r
3563    */\r
3564   Boolean fullrepaint = repaint;\r
3565 \r
3566   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3567 \r
3568   if( DrawPositionNeedsFullRepaint() ) {\r
3569       fullrepaint = TRUE;\r
3570   }\r
3571 \r
3572   if (board == NULL) {\r
3573     if (!lastReqValid[nr]) {\r
3574       return;\r
3575     }\r
3576     board = lastReq[nr];\r
3577   } else {\r
3578     CopyBoard(lastReq[nr], board);\r
3579     lastReqValid[nr] = 1;\r
3580   }\r
3581 \r
3582   if (doingSizing) {\r
3583     return;\r
3584   }\r
3585 \r
3586   if (IsIconic(hwndMain)) {\r
3587     return;\r
3588   }\r
3589 \r
3590   if (hdc == NULL) {\r
3591     hdc = GetDC(hwndMain);\r
3592     if (!appData.monoMode) {\r
3593       SelectPalette(hdc, hPal, FALSE);\r
3594       RealizePalette(hdc);\r
3595     }\r
3596     releaseDC = TRUE;\r
3597   } else {\r
3598     releaseDC = FALSE;\r
3599   }\r
3600 \r
3601   /* Create some work-DCs */\r
3602   hdcmem = CreateCompatibleDC(hdc);\r
3603   tmphdc = CreateCompatibleDC(hdc);\r
3604 \r
3605   /* If dragging is in progress, we temporarely remove the piece */\r
3606   /* [HGM] or temporarily decrease count if stacked              */\r
3607   /*       !! Moved to before board compare !!                   */\r
3608   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3609     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3610     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3611             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3612         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3613     } else \r
3614     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3615             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3616         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3617     } else \r
3618         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3619   }\r
3620 \r
3621   /* Figure out which squares need updating by comparing the \r
3622    * newest board with the last drawn board and checking if\r
3623    * flipping has changed.\r
3624    */\r
3625   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3626     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3627       for (column = 0; column < BOARD_WIDTH; column++) {\r
3628         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3629           SquareToPos(row, column, &x, &y);\r
3630           clips[num_clips++] =\r
3631             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3632         }\r
3633       }\r
3634     }\r
3635    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3636     for (i=0; i<2; i++) {\r
3637       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3638           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3639         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3640             lastDrawnHighlight.sq[i].y >= 0) {\r
3641           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3642                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3643           clips[num_clips++] =\r
3644             CreateRectRgn(x - lineGap, y - lineGap, \r
3645                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3646         }\r
3647         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3648           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3649           clips[num_clips++] =\r
3650             CreateRectRgn(x - lineGap, y - lineGap, \r
3651                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3652         }\r
3653       }\r
3654     }\r
3655     for (i=0; i<2; i++) {\r
3656       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3657           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3658         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3659             lastDrawnPremove.sq[i].y >= 0) {\r
3660           SquareToPos(lastDrawnPremove.sq[i].y,\r
3661                       lastDrawnPremove.sq[i].x, &x, &y);\r
3662           clips[num_clips++] =\r
3663             CreateRectRgn(x - lineGap, y - lineGap, \r
3664                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3665         }\r
3666         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3667             premoveHighlightInfo.sq[i].y >= 0) {\r
3668           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3669                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3670           clips[num_clips++] =\r
3671             CreateRectRgn(x - lineGap, y - lineGap, \r
3672                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3673         }\r
3674       }\r
3675     }\r
3676    } else { // nr == 1\r
3677         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3678         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3679         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3680         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3681       for (i=0; i<2; i++) {\r
3682         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3683             partnerHighlightInfo.sq[i].y >= 0) {\r
3684           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3685                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3686           clips[num_clips++] =\r
3687             CreateRectRgn(x - lineGap, y - lineGap, \r
3688                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3689         }\r
3690         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3691             oldPartnerHighlight.sq[i].y >= 0) {\r
3692           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3693                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3694           clips[num_clips++] =\r
3695             CreateRectRgn(x - lineGap, y - lineGap, \r
3696                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3697         }\r
3698       }\r
3699    }\r
3700   } else {\r
3701     fullrepaint = TRUE;\r
3702   }\r
3703 \r
3704   /* Create a buffer bitmap - this is the actual bitmap\r
3705    * being written to.  When all the work is done, we can\r
3706    * copy it to the real DC (the screen).  This avoids\r
3707    * the problems with flickering.\r
3708    */\r
3709   GetClientRect(hwndMain, &Rect);\r
3710   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3711                                         Rect.bottom-Rect.top+1);\r
3712   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3713   if (!appData.monoMode) {\r
3714     SelectPalette(hdcmem, hPal, FALSE);\r
3715   }\r
3716 \r
3717   /* Create clips for dragging */\r
3718   if (!fullrepaint) {\r
3719     if (dragInfo.from.x >= 0) {\r
3720       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3721       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3722     }\r
3723     if (dragInfo.start.x >= 0) {\r
3724       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3725       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3726     }\r
3727     if (dragInfo.pos.x >= 0) {\r
3728       x = dragInfo.pos.x - squareSize / 2;\r
3729       y = dragInfo.pos.y - squareSize / 2;\r
3730       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3731     }\r
3732     if (dragInfo.lastpos.x >= 0) {\r
3733       x = dragInfo.lastpos.x - squareSize / 2;\r
3734       y = dragInfo.lastpos.y - squareSize / 2;\r
3735       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3736     }\r
3737   }\r
3738 \r
3739   /* Are we animating a move?  \r
3740    * If so, \r
3741    *   - remove the piece from the board (temporarely)\r
3742    *   - calculate the clipping region\r
3743    */\r
3744   if (!fullrepaint) {\r
3745     if (animInfo.piece != EmptySquare) {\r
3746       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3747       x = boardRect.left + animInfo.lastpos.x;\r
3748       y = boardRect.top + animInfo.lastpos.y;\r
3749       x2 = boardRect.left + animInfo.pos.x;\r
3750       y2 = boardRect.top + animInfo.pos.y;\r
3751       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3752       /* Slight kludge.  The real problem is that after AnimateMove is\r
3753          done, the position on the screen does not match lastDrawn.\r
3754          This currently causes trouble only on e.p. captures in\r
3755          atomic, where the piece moves to an empty square and then\r
3756          explodes.  The old and new positions both had an empty square\r
3757          at the destination, but animation has drawn a piece there and\r
3758          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3759       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3760     }\r
3761   }\r
3762 \r
3763   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3764   if (num_clips == 0)\r
3765     fullrepaint = TRUE;\r
3766 \r
3767   /* Set clipping on the memory DC */\r
3768   if (!fullrepaint) {\r
3769     SelectClipRgn(hdcmem, clips[0]);\r
3770     for (x = 1; x < num_clips; x++) {\r
3771       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3772         abort();  // this should never ever happen!\r
3773     }\r
3774   }\r
3775 \r
3776   /* Do all the drawing to the memory DC */\r
3777   if(explodeInfo.radius) { // [HGM] atomic\r
3778         HBRUSH oldBrush;\r
3779         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3780         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3781         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3782         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3783         x += squareSize/2;\r
3784         y += squareSize/2;\r
3785         if(!fullrepaint) {\r
3786           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3787           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3788         }\r
3789         DrawGridOnDC(hdcmem);\r
3790         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3791         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3792         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3793         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3794         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3795         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3796         SelectObject(hdcmem, oldBrush);\r
3797   } else {\r
3798     DrawGridOnDC(hdcmem);\r
3799     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3800         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3801         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3802     } else {\r
3803         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3804         oldPartnerHighlight = partnerHighlightInfo;\r
3805     }\r
3806     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3807   }\r
3808   if(nr == 0) // [HGM] dual: markers only on left board\r
3809   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3810     for (column = 0; column < BOARD_WIDTH; column++) {\r
3811         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3812             HBRUSH oldBrush = SelectObject(hdcmem, \r
3813                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3814             SquareToPos(row, column, &x, &y);\r
3815             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3816                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3817             SelectObject(hdcmem, oldBrush);\r
3818         }\r
3819     }\r
3820   }\r
3821 \r
3822   if( appData.highlightMoveWithArrow ) {\r
3823     DrawArrowHighlight(hdcmem);\r
3824   }\r
3825 \r
3826   DrawCoordsOnDC(hdcmem);\r
3827 \r
3828   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3829                  /* to make sure lastDrawn contains what is actually drawn */\r
3830 \r
3831   /* Put the dragged piece back into place and draw it (out of place!) */\r
3832     if (dragged_piece != EmptySquare) {\r
3833     /* [HGM] or restack */\r
3834     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3835                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3836     else\r
3837     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3838                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3839     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3840     x = dragInfo.pos.x - squareSize / 2;\r
3841     y = dragInfo.pos.y - squareSize / 2;\r
3842     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3843                   ((int) dragInfo.piece < (int) BlackPawn), \r
3844                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3845   }   \r
3846   \r
3847   /* Put the animated piece back into place and draw it */\r
3848   if (animInfo.piece != EmptySquare) {\r
3849     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3850     x = boardRect.left + animInfo.pos.x;\r
3851     y = boardRect.top + animInfo.pos.y;\r
3852     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3853                   ((int) animInfo.piece < (int) BlackPawn),\r
3854                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3855   }\r
3856 \r
3857   /* Release the bufferBitmap by selecting in the old bitmap \r
3858    * and delete the memory DC\r
3859    */\r
3860   SelectObject(hdcmem, oldBitmap);\r
3861   DeleteDC(hdcmem);\r
3862 \r
3863   /* Set clipping on the target DC */\r
3864   if (!fullrepaint) {\r
3865     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3866         RECT rect;\r
3867         GetRgnBox(clips[x], &rect);\r
3868         DeleteObject(clips[x]);\r
3869         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3870                           rect.right + wpMain.width/2, rect.bottom);\r
3871     }\r
3872     SelectClipRgn(hdc, clips[0]);\r
3873     for (x = 1; x < num_clips; x++) {\r
3874       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3875         abort();   // this should never ever happen!\r
3876     } \r
3877   }\r
3878 \r
3879   /* Copy the new bitmap onto the screen in one go.\r
3880    * This way we avoid any flickering\r
3881    */\r
3882   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3883   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3884          boardRect.right - boardRect.left,\r
3885          boardRect.bottom - boardRect.top,\r
3886          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3887   if(saveDiagFlag) { \r
3888     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3889     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3890 \r
3891     GetObject(bufferBitmap, sizeof(b), &b);\r
3892     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3893         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3894         bih.biWidth = b.bmWidth;\r
3895         bih.biHeight = b.bmHeight;\r
3896         bih.biPlanes = 1;\r
3897         bih.biBitCount = b.bmBitsPixel;\r
3898         bih.biCompression = 0;\r
3899         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3900         bih.biXPelsPerMeter = 0;\r
3901         bih.biYPelsPerMeter = 0;\r
3902         bih.biClrUsed = 0;\r
3903         bih.biClrImportant = 0;\r
3904 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3905 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3906         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3907 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3908 \r
3909         wb = b.bmWidthBytes;\r
3910         // count colors\r
3911         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3912                 int k = ((int*) pData)[i];\r
3913                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3914                 if(j >= 16) break;\r
3915                 color[j] = k;\r
3916                 if(j >= nrColors) nrColors = j+1;\r
3917         }\r
3918         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3919                 INT p = 0;\r
3920                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3921                     for(w=0; w<(wb>>2); w+=2) {\r
3922                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3923                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3924                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3925                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3926                         pData[p++] = m | j<<4;\r
3927                     }\r
3928                     while(p&3) pData[p++] = 0;\r
3929                 }\r
3930                 fac = 3;\r
3931                 wb = ((wb+31)>>5)<<2;\r
3932         }\r
3933         // write BITMAPFILEHEADER\r
3934         fprintf(diagFile, "BM");\r
3935         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3936         fputDW(diagFile, 0);\r
3937         fputDW(diagFile, 0x36 + (fac?64:0));\r
3938         // write BITMAPINFOHEADER\r
3939         fputDW(diagFile, 40);\r
3940         fputDW(diagFile, b.bmWidth);\r
3941         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3942         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3943         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3944         fputDW(diagFile, 0);\r
3945         fputDW(diagFile, 0);\r
3946         fputDW(diagFile, 0);\r
3947         fputDW(diagFile, 0);\r
3948         fputDW(diagFile, 0);\r
3949         fputDW(diagFile, 0);\r
3950         // write color table\r
3951         if(fac)\r
3952         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3953         // write bitmap data\r
3954         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3955                 fputc(pData[i], diagFile);\r
3956         free(pData);\r
3957      }\r
3958   }\r
3959 \r
3960   SelectObject(tmphdc, oldBitmap);\r
3961 \r
3962   /* Massive cleanup */\r
3963   for (x = 0; x < num_clips; x++)\r
3964     DeleteObject(clips[x]);\r
3965 \r
3966   DeleteDC(tmphdc);\r
3967   DeleteObject(bufferBitmap);\r
3968 \r
3969   if (releaseDC) \r
3970     ReleaseDC(hwndMain, hdc);\r
3971   \r
3972   if (lastDrawnFlipView != flipView && nr == 0) {\r
3973     if (flipView)\r
3974       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3975     else\r
3976       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3977   }\r
3978 \r
3979 /*  CopyBoard(lastDrawn, board);*/\r
3980   lastDrawnHighlight = highlightInfo;\r
3981   lastDrawnPremove   = premoveHighlightInfo;\r
3982   lastDrawnFlipView = flipView;\r
3983   lastDrawnValid[nr] = 1;\r
3984 }\r
3985 \r
3986 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3987 int\r
3988 SaveDiagram(f)\r
3989      FILE *f;\r
3990 {\r
3991     saveDiagFlag = 1; diagFile = f;\r
3992     HDCDrawPosition(NULL, TRUE, NULL);\r
3993     saveDiagFlag = 0;\r
3994 \r
3995     fclose(f);\r
3996     return TRUE;\r
3997 }\r
3998 \r
3999 \r
4000 /*---------------------------------------------------------------------------*\\r
4001 | CLIENT PAINT PROCEDURE\r
4002 |   This is the main event-handler for the WM_PAINT message.\r
4003 |\r
4004 \*---------------------------------------------------------------------------*/\r
4005 VOID\r
4006 PaintProc(HWND hwnd)\r
4007 {\r
4008   HDC         hdc;\r
4009   PAINTSTRUCT ps;\r
4010   HFONT       oldFont;\r
4011 \r
4012   if((hdc = BeginPaint(hwnd, &ps))) {\r
4013     if (IsIconic(hwnd)) {\r
4014       DrawIcon(hdc, 2, 2, iconCurrent);\r
4015     } else {\r
4016       if (!appData.monoMode) {\r
4017         SelectPalette(hdc, hPal, FALSE);\r
4018         RealizePalette(hdc);\r
4019       }\r
4020       HDCDrawPosition(hdc, 1, NULL);\r
4021       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4022         flipView = !flipView; partnerUp = !partnerUp;\r
4023         HDCDrawPosition(hdc, 1, NULL);\r
4024         flipView = !flipView; partnerUp = !partnerUp;\r
4025       }\r
4026       oldFont =\r
4027         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4028       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4029                  ETO_CLIPPED|ETO_OPAQUE,\r
4030                  &messageRect, messageText, strlen(messageText), NULL);\r
4031       SelectObject(hdc, oldFont);\r
4032       DisplayBothClocks();\r
4033       DisplayLogos();\r
4034     }\r
4035     EndPaint(hwnd,&ps);\r
4036   }\r
4037 \r
4038   return;\r
4039 }\r
4040 \r
4041 \r
4042 /*\r
4043  * If the user selects on a border boundary, return -1; if off the board,\r
4044  *   return -2.  Otherwise map the event coordinate to the square.\r
4045  * The offset boardRect.left or boardRect.top must already have been\r
4046  *   subtracted from x.\r
4047  */\r
4048 int EventToSquare(x, limit)\r
4049      int x, limit;\r
4050 {\r
4051   if (x <= 0)\r
4052     return -2;\r
4053   if (x < lineGap)\r
4054     return -1;\r
4055   x -= lineGap;\r
4056   if ((x % (squareSize + lineGap)) >= squareSize)\r
4057     return -1;\r
4058   x /= (squareSize + lineGap);\r
4059     if (x >= limit)\r
4060     return -2;\r
4061   return x;\r
4062 }\r
4063 \r
4064 typedef struct {\r
4065   char piece;\r
4066   int command;\r
4067   char* name;\r
4068 } DropEnable;\r
4069 \r
4070 DropEnable dropEnables[] = {\r
4071   { 'P', DP_Pawn, N_("Pawn") },\r
4072   { 'N', DP_Knight, N_("Knight") },\r
4073   { 'B', DP_Bishop, N_("Bishop") },\r
4074   { 'R', DP_Rook, N_("Rook") },\r
4075   { 'Q', DP_Queen, N_("Queen") },\r
4076 };\r
4077 \r
4078 VOID\r
4079 SetupDropMenu(HMENU hmenu)\r
4080 {\r
4081   int i, count, enable;\r
4082   char *p;\r
4083   extern char white_holding[], black_holding[];\r
4084   char item[MSG_SIZ];\r
4085 \r
4086   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4087     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4088                dropEnables[i].piece);\r
4089     count = 0;\r
4090     while (p && *p++ == dropEnables[i].piece) count++;\r
4091       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4092     enable = count > 0 || !appData.testLegality\r
4093       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4094                       && !appData.icsActive);\r
4095     ModifyMenu(hmenu, dropEnables[i].command,\r
4096                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4097                dropEnables[i].command, item);\r
4098   }\r
4099 }\r
4100 \r
4101 void DragPieceBegin(int x, int y, Boolean instantly)\r
4102 {\r
4103       dragInfo.lastpos.x = boardRect.left + x;\r
4104       dragInfo.lastpos.y = boardRect.top + y;\r
4105       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4106       dragInfo.from.x = fromX;\r
4107       dragInfo.from.y = fromY;\r
4108       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4109       dragInfo.start = dragInfo.from;\r
4110       SetCapture(hwndMain);\r
4111 }\r
4112 \r
4113 void DragPieceEnd(int x, int y)\r
4114 {\r
4115     ReleaseCapture();\r
4116     dragInfo.start.x = dragInfo.start.y = -1;\r
4117     dragInfo.from = dragInfo.start;\r
4118     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4119 }\r
4120 \r
4121 void ChangeDragPiece(ChessSquare piece)\r
4122 {\r
4123     dragInfo.piece = piece;\r
4124 }\r
4125 \r
4126 /* Event handler for mouse messages */\r
4127 VOID\r
4128 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4129 {\r
4130   int x, y, menuNr;\r
4131   POINT pt;\r
4132   static int recursive = 0;\r
4133   HMENU hmenu;\r
4134   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4135 \r
4136   if (recursive) {\r
4137     if (message == WM_MBUTTONUP) {\r
4138       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4139          to the middle button: we simulate pressing the left button too!\r
4140          */\r
4141       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4142       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4143     }\r
4144     return;\r
4145   }\r
4146   recursive++;\r
4147   \r
4148   pt.x = LOWORD(lParam);\r
4149   pt.y = HIWORD(lParam);\r
4150   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4151   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4152   if (!flipView && y >= 0) {\r
4153     y = BOARD_HEIGHT - 1 - y;\r
4154   }\r
4155   if (flipView && x >= 0) {\r
4156     x = BOARD_WIDTH - 1 - x;\r
4157   }\r
4158 \r
4159   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4160 \r
4161   switch (message) {\r
4162   case WM_LBUTTONDOWN:\r
4163       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4164         ClockClick(flipClock); break;\r
4165       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4166         ClockClick(!flipClock); break;\r
4167       }\r
4168       dragInfo.start.x = dragInfo.start.y = -1;\r
4169       dragInfo.from = dragInfo.start;\r
4170     if(fromX == -1 && frozen) { // not sure where this is for\r
4171                 fromX = fromY = -1; \r
4172       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4173       break;\r
4174     }\r
4175       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4176       DrawPosition(TRUE, NULL);\r
4177     break;\r
4178 \r
4179   case WM_LBUTTONUP:\r
4180       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4181       DrawPosition(TRUE, NULL);\r
4182     break;\r
4183 \r
4184   case WM_MOUSEMOVE:\r
4185     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4186     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4187     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4188     if ((appData.animateDragging || appData.highlightDragging)\r
4189         && (wParam & MK_LBUTTON)\r
4190         && dragInfo.from.x >= 0) \r
4191     {\r
4192       BOOL full_repaint = FALSE;\r
4193 \r
4194       if (appData.animateDragging) {\r
4195         dragInfo.pos = pt;\r
4196       }\r
4197       if (appData.highlightDragging) {\r
4198         SetHighlights(fromX, fromY, x, y);\r
4199         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4200             full_repaint = TRUE;\r
4201         }\r
4202       }\r
4203       \r
4204       DrawPosition( full_repaint, NULL);\r
4205       \r
4206       dragInfo.lastpos = dragInfo.pos;\r
4207     }\r
4208     break;\r
4209 \r
4210   case WM_MOUSEWHEEL: // [DM]\r
4211     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4212        /* Mouse Wheel is being rolled forward\r
4213         * Play moves forward\r
4214         */\r
4215        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4216                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4217        /* Mouse Wheel is being rolled backward\r
4218         * Play moves backward\r
4219         */\r
4220        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4221                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4222     }\r
4223     break;\r
4224 \r
4225   case WM_MBUTTONUP:\r
4226   case WM_RBUTTONUP:\r
4227     ReleaseCapture();\r
4228     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4229     break;\r
4230  \r
4231   case WM_MBUTTONDOWN:\r
4232   case WM_RBUTTONDOWN:\r
4233     ErrorPopDown();\r
4234     ReleaseCapture();\r
4235     fromX = fromY = -1;\r
4236     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4237     dragInfo.start.x = dragInfo.start.y = -1;\r
4238     dragInfo.from = dragInfo.start;\r
4239     dragInfo.lastpos = dragInfo.pos;\r
4240     if (appData.highlightDragging) {\r
4241       ClearHighlights();\r
4242     }\r
4243     if(y == -2) {\r
4244       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4245       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4246           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4247       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4248           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4249       }\r
4250       break;\r
4251     }\r
4252     DrawPosition(TRUE, NULL);\r
4253 \r
4254     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4255     switch (menuNr) {\r
4256     case 0:\r
4257       if (message == WM_MBUTTONDOWN) {\r
4258         buttonCount = 3;  /* even if system didn't think so */\r
4259         if (wParam & MK_SHIFT) \r
4260           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4261         else\r
4262           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4263       } else { /* message == WM_RBUTTONDOWN */\r
4264         /* Just have one menu, on the right button.  Windows users don't\r
4265            think to try the middle one, and sometimes other software steals\r
4266            it, or it doesn't really exist. */\r
4267         if(gameInfo.variant != VariantShogi)\r
4268             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4269         else\r
4270             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4271       }\r
4272       break;\r
4273     case 2:\r
4274       SetCapture(hwndMain);\r
4275       break;\r
4276     case 1:\r
4277       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4278       SetupDropMenu(hmenu);\r
4279       MenuPopup(hwnd, pt, hmenu, -1);\r
4280     default:\r
4281       break;\r
4282     }\r
4283     break;\r
4284   }\r
4285 \r
4286   recursive--;\r
4287 }\r
4288 \r
4289 /* Preprocess messages for buttons in main window */\r
4290 LRESULT CALLBACK\r
4291 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4292 {\r
4293   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4294   int i, dir;\r
4295 \r
4296   for (i=0; i<N_BUTTONS; i++) {\r
4297     if (buttonDesc[i].id == id) break;\r
4298   }\r
4299   if (i == N_BUTTONS) return 0;\r
4300   switch (message) {\r
4301   case WM_KEYDOWN:\r
4302     switch (wParam) {\r
4303     case VK_LEFT:\r
4304     case VK_RIGHT:\r
4305       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4306       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4307       return TRUE;\r
4308     }\r
4309     break;\r
4310   case WM_CHAR:\r
4311     switch (wParam) {\r
4312     case '\r':\r
4313       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4314       return TRUE;\r
4315     default:\r
4316       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4317         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4318         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4319         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4320         SetFocus(h);\r
4321         SendMessage(h, WM_CHAR, wParam, lParam);\r
4322         return TRUE;\r
4323       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4324         TypeInEvent((char)wParam);\r
4325       }\r
4326       break;\r
4327     }\r
4328     break;\r
4329   }\r
4330   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4331 }\r
4332 \r
4333 /* Process messages for Promotion dialog box */\r
4334 LRESULT CALLBACK\r
4335 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4336 {\r
4337   char promoChar;\r
4338 \r
4339   switch (message) {\r
4340   case WM_INITDIALOG: /* message: initialize dialog box */\r
4341     /* Center the dialog over the application window */\r
4342     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4343     Translate(hDlg, DLG_PromotionKing);\r
4344     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4345       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4346        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4347        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4348                SW_SHOW : SW_HIDE);\r
4349     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4350     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4351        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4352          PieceToChar(WhiteAngel) != '~') ||\r
4353         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4354          PieceToChar(BlackAngel) != '~')   ) ?\r
4355                SW_SHOW : SW_HIDE);\r
4356     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4357        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4358          PieceToChar(WhiteMarshall) != '~') ||\r
4359         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4360          PieceToChar(BlackMarshall) != '~')   ) ?\r
4361                SW_SHOW : SW_HIDE);\r
4362     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4363     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4364        gameInfo.variant != VariantShogi ?\r
4365                SW_SHOW : SW_HIDE);\r
4366     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4367        gameInfo.variant != VariantShogi ?\r
4368                SW_SHOW : SW_HIDE);\r
4369     if(gameInfo.variant == VariantShogi) {\r
4370         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4371         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4372         SetWindowText(hDlg, "Promote?");\r
4373     }\r
4374     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4375        gameInfo.variant == VariantSuper ?\r
4376                SW_SHOW : SW_HIDE);\r
4377     return TRUE;\r
4378 \r
4379   case WM_COMMAND: /* message: received a command */\r
4380     switch (LOWORD(wParam)) {\r
4381     case IDCANCEL:\r
4382       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4383       ClearHighlights();\r
4384       DrawPosition(FALSE, NULL);\r
4385       return TRUE;\r
4386     case PB_King:\r
4387       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4388       break;\r
4389     case PB_Queen:\r
4390       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4391       break;\r
4392     case PB_Rook:\r
4393       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4394       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4395       break;\r
4396     case PB_Bishop:\r
4397       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4398       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4399       break;\r
4400     case PB_Chancellor:\r
4401       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4402       break;\r
4403     case PB_Archbishop:\r
4404       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4405       break;\r
4406     case PB_Knight:\r
4407       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4408       break;\r
4409     default:\r
4410       return FALSE;\r
4411     }\r
4412     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4413     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4414     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4415     fromX = fromY = -1;\r
4416     if (!appData.highlightLastMove) {\r
4417       ClearHighlights();\r
4418       DrawPosition(FALSE, NULL);\r
4419     }\r
4420     return TRUE;\r
4421   }\r
4422   return FALSE;\r
4423 }\r
4424 \r
4425 /* Pop up promotion dialog */\r
4426 VOID\r
4427 PromotionPopup(HWND hwnd)\r
4428 {\r
4429   FARPROC lpProc;\r
4430 \r
4431   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4432   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4433     hwnd, (DLGPROC)lpProc);\r
4434   FreeProcInstance(lpProc);\r
4435 }\r
4436 \r
4437 void\r
4438 PromotionPopUp()\r
4439 {\r
4440   DrawPosition(TRUE, NULL);\r
4441   PromotionPopup(hwndMain);\r
4442 }\r
4443 \r
4444 /* Toggle ShowThinking */\r
4445 VOID\r
4446 ToggleShowThinking()\r
4447 {\r
4448   appData.showThinking = !appData.showThinking;\r
4449   ShowThinkingEvent();\r
4450 }\r
4451 \r
4452 VOID\r
4453 LoadGameDialog(HWND hwnd, char* title)\r
4454 {\r
4455   UINT number = 0;\r
4456   FILE *f;\r
4457   char fileTitle[MSG_SIZ];\r
4458   f = OpenFileDialog(hwnd, "rb", "",\r
4459                      appData.oldSaveStyle ? "gam" : "pgn",\r
4460                      GAME_FILT,\r
4461                      title, &number, fileTitle, NULL);\r
4462   if (f != NULL) {\r
4463     cmailMsgLoaded = FALSE;\r
4464     if (number == 0) {\r
4465       int error = GameListBuild(f);\r
4466       if (error) {\r
4467         DisplayError(_("Cannot build game list"), error);\r
4468       } else if (!ListEmpty(&gameList) &&\r
4469                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4470         GameListPopUp(f, fileTitle);\r
4471         return;\r
4472       }\r
4473       GameListDestroy();\r
4474       number = 1;\r
4475     }\r
4476     LoadGame(f, number, fileTitle, FALSE);\r
4477   }\r
4478 }\r
4479 \r
4480 int get_term_width()\r
4481 {\r
4482     HDC hdc;\r
4483     TEXTMETRIC tm;\r
4484     RECT rc;\r
4485     HFONT hfont, hold_font;\r
4486     LOGFONT lf;\r
4487     HWND hText;\r
4488 \r
4489     if (hwndConsole)\r
4490         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4491     else\r
4492         return 79;\r
4493 \r
4494     // get the text metrics\r
4495     hdc = GetDC(hText);\r
4496     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4497     if (consoleCF.dwEffects & CFE_BOLD)\r
4498         lf.lfWeight = FW_BOLD;\r
4499     if (consoleCF.dwEffects & CFE_ITALIC)\r
4500         lf.lfItalic = TRUE;\r
4501     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4502         lf.lfStrikeOut = TRUE;\r
4503     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4504         lf.lfUnderline = TRUE;\r
4505     hfont = CreateFontIndirect(&lf);\r
4506     hold_font = SelectObject(hdc, hfont);\r
4507     GetTextMetrics(hdc, &tm);\r
4508     SelectObject(hdc, hold_font);\r
4509     DeleteObject(hfont);\r
4510     ReleaseDC(hText, hdc);\r
4511 \r
4512     // get the rectangle\r
4513     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4514 \r
4515     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4516 }\r
4517 \r
4518 void UpdateICSWidth(HWND hText)\r
4519 {\r
4520     LONG old_width, new_width;\r
4521 \r
4522     new_width = get_term_width(hText, FALSE);\r
4523     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4524     if (new_width != old_width)\r
4525     {\r
4526         ics_update_width(new_width);\r
4527         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4528     }\r
4529 }\r
4530 \r
4531 VOID\r
4532 ChangedConsoleFont()\r
4533 {\r
4534   CHARFORMAT cfmt;\r
4535   CHARRANGE tmpsel, sel;\r
4536   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4537   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4538   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4539   PARAFORMAT paraf;\r
4540 \r
4541   cfmt.cbSize = sizeof(CHARFORMAT);\r
4542   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4543     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4544                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4545   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4546    * size.  This was undocumented in the version of MSVC++ that I had\r
4547    * when I wrote the code, but is apparently documented now.\r
4548    */\r
4549   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4550   cfmt.bCharSet = f->lf.lfCharSet;\r
4551   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4552   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4553   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4554   /* Why are the following seemingly needed too? */\r
4555   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4556   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4557   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4558   tmpsel.cpMin = 0;\r
4559   tmpsel.cpMax = -1; /*999999?*/\r
4560   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4561   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4562   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4563    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4564    */\r
4565   paraf.cbSize = sizeof(paraf);\r
4566   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4567   paraf.dxStartIndent = 0;\r
4568   paraf.dxOffset = WRAP_INDENT;\r
4569   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4570   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4571   UpdateICSWidth(hText);\r
4572 }\r
4573 \r
4574 /*---------------------------------------------------------------------------*\\r
4575  *\r
4576  * Window Proc for main window\r
4577  *\r
4578 \*---------------------------------------------------------------------------*/\r
4579 \r
4580 /* Process messages for main window, etc. */\r
4581 LRESULT CALLBACK\r
4582 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4583 {\r
4584   FARPROC lpProc;\r
4585   int wmId, wmEvent;\r
4586   char *defName;\r
4587   FILE *f;\r
4588   UINT number;\r
4589   char fileTitle[MSG_SIZ];\r
4590   char buf[MSG_SIZ];\r
4591   static SnapData sd;\r
4592   static int peek=0;\r
4593 \r
4594   switch (message) {\r
4595 \r
4596   case WM_PAINT: /* message: repaint portion of window */\r
4597     PaintProc(hwnd);\r
4598     break;\r
4599 \r
4600   case WM_ERASEBKGND:\r
4601     if (IsIconic(hwnd)) {\r
4602       /* Cheat; change the message */\r
4603       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4604     } else {\r
4605       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4606     }\r
4607     break;\r
4608 \r
4609   case WM_LBUTTONDOWN:\r
4610   case WM_MBUTTONDOWN:\r
4611   case WM_RBUTTONDOWN:\r
4612   case WM_LBUTTONUP:\r
4613   case WM_MBUTTONUP:\r
4614   case WM_RBUTTONUP:\r
4615   case WM_MOUSEMOVE:\r
4616   case WM_MOUSEWHEEL:\r
4617     MouseEvent(hwnd, message, wParam, lParam);\r
4618     break;\r
4619 \r
4620   case WM_KEYUP:\r
4621     if((char)wParam == '\b') {\r
4622       ForwardEvent(); peek = 0;\r
4623     }\r
4624 \r
4625     JAWS_KBUP_NAVIGATION\r
4626 \r
4627     break;\r
4628 \r
4629   case WM_KEYDOWN:\r
4630     if((char)wParam == '\b') {\r
4631       if(!peek) BackwardEvent(), peek = 1;\r
4632     }\r
4633 \r
4634     JAWS_KBDOWN_NAVIGATION\r
4635 \r
4636     break;\r
4637 \r
4638   case WM_CHAR:\r
4639     \r
4640     JAWS_ALT_INTERCEPT\r
4641 \r
4642     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4643         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4644         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4645         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4646         SetFocus(h);\r
4647         SendMessage(h, message, wParam, lParam);\r
4648     } else if(lParam != KF_REPEAT) {\r
4649         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4650                 TypeInEvent((char)wParam);\r
4651         } else if((char)wParam == 003) CopyGameToClipboard();\r
4652          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4653     }\r
4654 \r
4655     break;\r
4656 \r
4657   case WM_PALETTECHANGED:\r
4658     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4659       int nnew;\r
4660       HDC hdc = GetDC(hwndMain);\r
4661       SelectPalette(hdc, hPal, TRUE);\r
4662       nnew = RealizePalette(hdc);\r
4663       if (nnew > 0) {\r
4664         paletteChanged = TRUE;\r
4665         InvalidateRect(hwnd, &boardRect, FALSE);\r
4666       }\r
4667       ReleaseDC(hwnd, hdc);\r
4668     }\r
4669     break;\r
4670 \r
4671   case WM_QUERYNEWPALETTE:\r
4672     if (!appData.monoMode /*&& paletteChanged*/) {\r
4673       int nnew;\r
4674       HDC hdc = GetDC(hwndMain);\r
4675       paletteChanged = FALSE;\r
4676       SelectPalette(hdc, hPal, FALSE);\r
4677       nnew = RealizePalette(hdc);\r
4678       if (nnew > 0) {\r
4679         InvalidateRect(hwnd, &boardRect, FALSE);\r
4680       }\r
4681       ReleaseDC(hwnd, hdc);\r
4682       return TRUE;\r
4683     }\r
4684     return FALSE;\r
4685 \r
4686   case WM_COMMAND: /* message: command from application menu */\r
4687     wmId    = LOWORD(wParam);\r
4688     wmEvent = HIWORD(wParam);\r
4689 \r
4690     switch (wmId) {\r
4691     case IDM_NewGame:\r
4692       ResetGameEvent();\r
4693       SAY("new game enter a move to play against the computer with white");\r
4694       break;\r
4695 \r
4696     case IDM_NewGameFRC:\r
4697       if( NewGameFRC() == 0 ) {\r
4698         ResetGameEvent();\r
4699       }\r
4700       break;\r
4701 \r
4702     case IDM_NewVariant:\r
4703       NewVariantPopup(hwnd);\r
4704       break;\r
4705 \r
4706     case IDM_LoadGame:\r
4707       LoadGameDialog(hwnd, _("Load Game from File"));\r
4708       break;\r
4709 \r
4710     case IDM_LoadNextGame:\r
4711       ReloadGame(1);\r
4712       break;\r
4713 \r
4714     case IDM_LoadPrevGame:\r
4715       ReloadGame(-1);\r
4716       break;\r
4717 \r
4718     case IDM_ReloadGame:\r
4719       ReloadGame(0);\r
4720       break;\r
4721 \r
4722     case IDM_LoadPosition:\r
4723       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4724         Reset(FALSE, TRUE);\r
4725       }\r
4726       number = 1;\r
4727       f = OpenFileDialog(hwnd, "rb", "",\r
4728                          appData.oldSaveStyle ? "pos" : "fen",\r
4729                          POSITION_FILT,\r
4730                          _("Load Position from File"), &number, fileTitle, NULL);\r
4731       if (f != NULL) {\r
4732         LoadPosition(f, number, fileTitle);\r
4733       }\r
4734       break;\r
4735 \r
4736     case IDM_LoadNextPosition:\r
4737       ReloadPosition(1);\r
4738       break;\r
4739 \r
4740     case IDM_LoadPrevPosition:\r
4741       ReloadPosition(-1);\r
4742       break;\r
4743 \r
4744     case IDM_ReloadPosition:\r
4745       ReloadPosition(0);\r
4746       break;\r
4747 \r
4748     case IDM_SaveGame:\r
4749       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4750       f = OpenFileDialog(hwnd, "a", defName,\r
4751                          appData.oldSaveStyle ? "gam" : "pgn",\r
4752                          GAME_FILT,\r
4753                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4754       if (f != NULL) {\r
4755         SaveGame(f, 0, "");\r
4756       }\r
4757       break;\r
4758 \r
4759     case IDM_SavePosition:\r
4760       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4761       f = OpenFileDialog(hwnd, "a", defName,\r
4762                          appData.oldSaveStyle ? "pos" : "fen",\r
4763                          POSITION_FILT,\r
4764                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4765       if (f != NULL) {\r
4766         SavePosition(f, 0, "");\r
4767       }\r
4768       break;\r
4769 \r
4770     case IDM_SaveDiagram:\r
4771       defName = "diagram";\r
4772       f = OpenFileDialog(hwnd, "wb", defName,\r
4773                          "bmp",\r
4774                          DIAGRAM_FILT,\r
4775                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4776       if (f != NULL) {\r
4777         SaveDiagram(f);\r
4778       }\r
4779       break;\r
4780 \r
4781     case IDM_CopyGame:\r
4782       CopyGameToClipboard();\r
4783       break;\r
4784 \r
4785     case IDM_PasteGame:\r
4786       PasteGameFromClipboard();\r
4787       break;\r
4788 \r
4789     case IDM_CopyGameListToClipboard:\r
4790       CopyGameListToClipboard();\r
4791       break;\r
4792 \r
4793     /* [AS] Autodetect FEN or PGN data */\r
4794     case IDM_PasteAny:\r
4795       PasteGameOrFENFromClipboard();\r
4796       break;\r
4797 \r
4798     /* [AS] Move history */\r
4799     case IDM_ShowMoveHistory:\r
4800         if( MoveHistoryIsUp() ) {\r
4801             MoveHistoryPopDown();\r
4802         }\r
4803         else {\r
4804             MoveHistoryPopUp();\r
4805         }\r
4806         break;\r
4807 \r
4808     /* [AS] Eval graph */\r
4809     case IDM_ShowEvalGraph:\r
4810         if( EvalGraphIsUp() ) {\r
4811             EvalGraphPopDown();\r
4812         }\r
4813         else {\r
4814             EvalGraphPopUp();\r
4815             SetFocus(hwndMain);\r
4816         }\r
4817         break;\r
4818 \r
4819     /* [AS] Engine output */\r
4820     case IDM_ShowEngineOutput:\r
4821         if( EngineOutputIsUp() ) {\r
4822             EngineOutputPopDown();\r
4823         }\r
4824         else {\r
4825             EngineOutputPopUp();\r
4826         }\r
4827         break;\r
4828 \r
4829     /* [AS] User adjudication */\r
4830     case IDM_UserAdjudication_White:\r
4831         UserAdjudicationEvent( +1 );\r
4832         break;\r
4833 \r
4834     case IDM_UserAdjudication_Black:\r
4835         UserAdjudicationEvent( -1 );\r
4836         break;\r
4837 \r
4838     case IDM_UserAdjudication_Draw:\r
4839         UserAdjudicationEvent( 0 );\r
4840         break;\r
4841 \r
4842     /* [AS] Game list options dialog */\r
4843     case IDM_GameListOptions:\r
4844       GameListOptions();\r
4845       break;\r
4846 \r
4847     case IDM_NewChat:\r
4848       ChatPopUp(NULL);\r
4849       break;\r
4850 \r
4851     case IDM_CopyPosition:\r
4852       CopyFENToClipboard();\r
4853       break;\r
4854 \r
4855     case IDM_PastePosition:\r
4856       PasteFENFromClipboard();\r
4857       break;\r
4858 \r
4859     case IDM_MailMove:\r
4860       MailMoveEvent();\r
4861       break;\r
4862 \r
4863     case IDM_ReloadCMailMsg:\r
4864       Reset(TRUE, TRUE);\r
4865       ReloadCmailMsgEvent(FALSE);\r
4866       break;\r
4867 \r
4868     case IDM_Minimize:\r
4869       ShowWindow(hwnd, SW_MINIMIZE);\r
4870       break;\r
4871 \r
4872     case IDM_Exit:\r
4873       ExitEvent(0);\r
4874       break;\r
4875 \r
4876     case IDM_MachineWhite:\r
4877       MachineWhiteEvent();\r
4878       /*\r
4879        * refresh the tags dialog only if it's visible\r
4880        */\r
4881       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4882           char *tags;\r
4883           tags = PGNTags(&gameInfo);\r
4884           TagsPopUp(tags, CmailMsg());\r
4885           free(tags);\r
4886       }\r
4887       SAY("computer starts playing white");\r
4888       break;\r
4889 \r
4890     case IDM_MachineBlack:\r
4891       MachineBlackEvent();\r
4892       /*\r
4893        * refresh the tags dialog only if it's visible\r
4894        */\r
4895       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4896           char *tags;\r
4897           tags = PGNTags(&gameInfo);\r
4898           TagsPopUp(tags, CmailMsg());\r
4899           free(tags);\r
4900       }\r
4901       SAY("computer starts playing black");\r
4902       break;\r
4903 \r
4904     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4905       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4906       break;\r
4907 \r
4908     case IDM_TwoMachines:\r
4909       TwoMachinesEvent();\r
4910       /*\r
4911        * refresh the tags dialog only if it's visible\r
4912        */\r
4913       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4914           char *tags;\r
4915           tags = PGNTags(&gameInfo);\r
4916           TagsPopUp(tags, CmailMsg());\r
4917           free(tags);\r
4918       }\r
4919       SAY("computer starts playing both sides");\r
4920       break;\r
4921 \r
4922     case IDM_AnalysisMode:\r
4923       if (!first.analysisSupport) {\r
4924         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4925         DisplayError(buf, 0);\r
4926       } else {\r
4927         SAY("analyzing current position");\r
4928         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4929         if (appData.icsActive) {\r
4930                if (gameMode != IcsObserving) {\r
4931                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4932                        DisplayError(buf, 0);\r
4933                        /* secure check */\r
4934                        if (appData.icsEngineAnalyze) {\r
4935                                if (appData.debugMode) \r
4936                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4937                                ExitAnalyzeMode();\r
4938                                ModeHighlight();\r
4939                                break;\r
4940                        }\r
4941                        break;\r
4942                } else {\r
4943                        /* if enable, user want disable icsEngineAnalyze */\r
4944                        if (appData.icsEngineAnalyze) {\r
4945                                ExitAnalyzeMode();\r
4946                                ModeHighlight();\r
4947                                break;\r
4948                        }\r
4949                        appData.icsEngineAnalyze = TRUE;\r
4950                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4951                }\r
4952         } \r
4953         if (!appData.showThinking) ToggleShowThinking();\r
4954         AnalyzeModeEvent();\r
4955       }\r
4956       break;\r
4957 \r
4958     case IDM_AnalyzeFile:\r
4959       if (!first.analysisSupport) {\r
4960         char buf[MSG_SIZ];\r
4961           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4962         DisplayError(buf, 0);\r
4963       } else {\r
4964         if (!appData.showThinking) ToggleShowThinking();\r
4965         AnalyzeFileEvent();\r
4966 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4967         AnalysisPeriodicEvent(1);\r
4968       }\r
4969       break;\r
4970 \r
4971     case IDM_IcsClient:\r
4972       IcsClientEvent();\r
4973       break;\r
4974 \r
4975     case IDM_EditGame:\r
4976     case IDM_EditGame2:\r
4977       EditGameEvent();\r
4978       SAY("edit game");\r
4979       break;\r
4980 \r
4981     case IDM_EditPosition:\r
4982     case IDM_EditPosition2:\r
4983       EditPositionEvent();\r
4984       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4985       break;\r
4986 \r
4987     case IDM_Training:\r
4988       TrainingEvent();\r
4989       break;\r
4990 \r
4991     case IDM_ShowGameList:\r
4992       ShowGameListProc();\r
4993       break;\r
4994 \r
4995     case IDM_EditProgs1:\r
4996       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4997       break;\r
4998 \r
4999     case IDM_EditProgs2:\r
5000      LoadEnginePopUp(hwndMain);\r
5001 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
5002       break;\r
5003 \r
5004     case IDM_EditServers:\r
5005       EditTagsPopUp(icsNames, &icsNames);\r
5006       break;\r
5007 \r
5008     case IDM_EditTags:\r
5009     case IDM_Tags:\r
5010       EditTagsProc();\r
5011       break;\r
5012 \r
5013     case IDM_EditBook:\r
5014       EditBookEvent();\r
5015       break;\r
5016 \r
5017     case IDM_EditComment:\r
5018     case IDM_Comment:\r
5019       if (commentUp && editComment) {\r
5020         CommentPopDown();\r
5021       } else {\r
5022         EditCommentEvent();\r
5023       }\r
5024       break;\r
5025 \r
5026     case IDM_Pause:\r
5027       PauseEvent();\r
5028       break;\r
5029 \r
5030     case IDM_Accept:\r
5031       AcceptEvent();\r
5032       break;\r
5033 \r
5034     case IDM_Decline:\r
5035       DeclineEvent();\r
5036       break;\r
5037 \r
5038     case IDM_Rematch:\r
5039       RematchEvent();\r
5040       break;\r
5041 \r
5042     case IDM_CallFlag:\r
5043       CallFlagEvent();\r
5044       break;\r
5045 \r
5046     case IDM_Draw:\r
5047       DrawEvent();\r
5048       break;\r
5049 \r
5050     case IDM_Adjourn:\r
5051       AdjournEvent();\r
5052       break;\r
5053 \r
5054     case IDM_Abort:\r
5055       AbortEvent();\r
5056       break;\r
5057 \r
5058     case IDM_Resign:\r
5059       ResignEvent();\r
5060       break;\r
5061 \r
5062     case IDM_StopObserving:\r
5063       StopObservingEvent();\r
5064       break;\r
5065 \r
5066     case IDM_StopExamining:\r
5067       StopExaminingEvent();\r
5068       break;\r
5069 \r
5070     case IDM_Upload:\r
5071       UploadGameEvent();\r
5072       break;\r
5073 \r
5074     case IDM_TypeInMove:\r
5075       TypeInEvent('\000');\r
5076       break;\r
5077 \r
5078     case IDM_TypeInName:\r
5079       PopUpNameDialog('\000');\r
5080       break;\r
5081 \r
5082     case IDM_Backward:\r
5083       BackwardEvent();\r
5084       SetFocus(hwndMain);\r
5085       break;\r
5086 \r
5087     JAWS_MENU_ITEMS\r
5088 \r
5089     case IDM_Forward:\r
5090       ForwardEvent();\r
5091       SetFocus(hwndMain);\r
5092       break;\r
5093 \r
5094     case IDM_ToStart:\r
5095       ToStartEvent();\r
5096       SetFocus(hwndMain);\r
5097       break;\r
5098 \r
5099     case IDM_ToEnd:\r
5100       ToEndEvent();\r
5101       SetFocus(hwndMain);\r
5102       break;\r
5103 \r
5104     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5105     case OPT_GameListPrev:\r
5106       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5107       break;\r
5108 \r
5109     case IDM_Revert:\r
5110       RevertEvent(FALSE);\r
5111       break;\r
5112 \r
5113     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5114       RevertEvent(TRUE);\r
5115       break;\r
5116 \r
5117     case IDM_TruncateGame:\r
5118       TruncateGameEvent();\r
5119       break;\r
5120 \r
5121     case IDM_MoveNow:\r
5122       MoveNowEvent();\r
5123       break;\r
5124 \r
5125     case IDM_RetractMove:\r
5126       RetractMoveEvent();\r
5127       break;\r
5128 \r
5129     case IDM_FlipView:\r
5130       flipView = !flipView;\r
5131       DrawPosition(FALSE, NULL);\r
5132       break;\r
5133 \r
5134     case IDM_FlipClock:\r
5135       flipClock = !flipClock;\r
5136       DisplayBothClocks();\r
5137       DisplayLogos();\r
5138       break;\r
5139 \r
5140     case IDM_MuteSounds:\r
5141       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5142       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5143                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5144       break;\r
5145 \r
5146     case IDM_GeneralOptions:\r
5147       GeneralOptionsPopup(hwnd);\r
5148       DrawPosition(TRUE, NULL);\r
5149       break;\r
5150 \r
5151     case IDM_BoardOptions:\r
5152       BoardOptionsPopup(hwnd);\r
5153       break;\r
5154 \r
5155     case IDM_EnginePlayOptions:\r
5156       EnginePlayOptionsPopup(hwnd);\r
5157       break;\r
5158 \r
5159     case IDM_Engine1Options:\r
5160       EngineOptionsPopup(hwnd, &first);\r
5161       break;\r
5162 \r
5163     case IDM_Engine2Options:\r
5164       savedHwnd = hwnd;\r
5165       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5166       EngineOptionsPopup(hwnd, &second);\r
5167       break;\r
5168 \r
5169     case IDM_OptionsUCI:\r
5170       UciOptionsPopup(hwnd);\r
5171       break;\r
5172 \r
5173     case IDM_Tourney:\r
5174       TourneyPopup(hwnd);\r
5175       break;\r
5176 \r
5177     case IDM_IcsOptions:\r
5178       IcsOptionsPopup(hwnd);\r
5179       break;\r
5180 \r
5181     case IDM_Fonts:\r
5182       FontsOptionsPopup(hwnd);\r
5183       break;\r
5184 \r
5185     case IDM_Sounds:\r
5186       SoundOptionsPopup(hwnd);\r
5187       break;\r
5188 \r
5189     case IDM_CommPort:\r
5190       CommPortOptionsPopup(hwnd);\r
5191       break;\r
5192 \r
5193     case IDM_LoadOptions:\r
5194       LoadOptionsPopup(hwnd);\r
5195       break;\r
5196 \r
5197     case IDM_SaveOptions:\r
5198       SaveOptionsPopup(hwnd);\r
5199       break;\r
5200 \r
5201     case IDM_TimeControl:\r
5202       TimeControlOptionsPopup(hwnd);\r
5203       break;\r
5204 \r
5205     case IDM_SaveSettings:\r
5206       SaveSettings(settingsFileName);\r
5207       break;\r
5208 \r
5209     case IDM_SaveSettingsOnExit:\r
5210       saveSettingsOnExit = !saveSettingsOnExit;\r
5211       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5212                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5213                                          MF_CHECKED : MF_UNCHECKED));\r
5214       break;\r
5215 \r
5216     case IDM_Hint:\r
5217       HintEvent();\r
5218       break;\r
5219 \r
5220     case IDM_Book:\r
5221       BookEvent();\r
5222       break;\r
5223 \r
5224     case IDM_AboutGame:\r
5225       AboutGameEvent();\r
5226       break;\r
5227 \r
5228     case IDM_Debug:\r
5229       appData.debugMode = !appData.debugMode;\r
5230       if (appData.debugMode) {\r
5231         char dir[MSG_SIZ];\r
5232         GetCurrentDirectory(MSG_SIZ, dir);\r
5233         SetCurrentDirectory(installDir);\r
5234         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5235         SetCurrentDirectory(dir);\r
5236         setbuf(debugFP, NULL);\r
5237       } else {\r
5238         fclose(debugFP);\r
5239         debugFP = NULL;\r
5240       }\r
5241       break;\r
5242 \r
5243     case IDM_HELPCONTENTS:\r
5244       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5245           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5246           MessageBox (GetFocus(),\r
5247                     _("Unable to activate help"),\r
5248                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5249       }\r
5250       break;\r
5251 \r
5252     case IDM_HELPSEARCH:\r
5253         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5254             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5255         MessageBox (GetFocus(),\r
5256                     _("Unable to activate help"),\r
5257                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5258       }\r
5259       break;\r
5260 \r
5261     case IDM_HELPHELP:\r
5262       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5263         MessageBox (GetFocus(),\r
5264                     _("Unable to activate help"),\r
5265                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5266       }\r
5267       break;\r
5268 \r
5269     case IDM_ABOUT:\r
5270       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5271       DialogBox(hInst, \r
5272         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5273         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5274       FreeProcInstance(lpProc);\r
5275       break;\r
5276 \r
5277     case IDM_DirectCommand1:\r
5278       AskQuestionEvent(_("Direct Command"),\r
5279                        _("Send to chess program:"), "", "1");\r
5280       break;\r
5281     case IDM_DirectCommand2:\r
5282       AskQuestionEvent(_("Direct Command"),\r
5283                        _("Send to second chess program:"), "", "2");\r
5284       break;\r
5285 \r
5286     case EP_WhitePawn:\r
5287       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_WhiteKnight:\r
5292       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_WhiteBishop:\r
5297       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_WhiteRook:\r
5302       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_WhiteQueen:\r
5307       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_WhiteFerz:\r
5312       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_WhiteWazir:\r
5317       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_WhiteAlfil:\r
5322       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_WhiteCannon:\r
5327       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_WhiteCardinal:\r
5332       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_WhiteMarshall:\r
5337       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_WhiteKing:\r
5342       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_BlackPawn:\r
5347       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_BlackKnight:\r
5352       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_BlackBishop:\r
5357       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_BlackRook:\r
5362       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_BlackQueen:\r
5367       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_BlackFerz:\r
5372       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_BlackWazir:\r
5377       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_BlackAlfil:\r
5382       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_BlackCannon:\r
5387       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_BlackCardinal:\r
5392       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_BlackMarshall:\r
5397       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_BlackKing:\r
5402       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_EmptySquare:\r
5407       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_ClearBoard:\r
5412       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_White:\r
5417       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_Black:\r
5422       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_Promote:\r
5427       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_Demote:\r
5432       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case DP_Pawn:\r
5437       DropMenuEvent(WhitePawn, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case DP_Knight:\r
5442       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case DP_Bishop:\r
5447       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case DP_Rook:\r
5452       DropMenuEvent(WhiteRook, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case DP_Queen:\r
5457       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case IDM_English:\r
5462       barbaric = 0; appData.language = "";\r
5463       TranslateMenus(0);\r
5464       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5465       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5466       lastChecked = wmId;\r
5467       break;\r
5468 \r
5469     default:\r
5470       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5471           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5472       else\r
5473       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5474           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5475           TranslateMenus(0);\r
5476           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5477           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5478           lastChecked = wmId;\r
5479           break;\r
5480       }\r
5481       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5482     }\r
5483     break;\r
5484 \r
5485   case WM_TIMER:\r
5486     switch (wParam) {\r
5487     case CLOCK_TIMER_ID:\r
5488       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5489       clockTimerEvent = 0;\r
5490       DecrementClocks(); /* call into back end */\r
5491       break;\r
5492     case LOAD_GAME_TIMER_ID:\r
5493       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5494       loadGameTimerEvent = 0;\r
5495       AutoPlayGameLoop(); /* call into back end */\r
5496       break;\r
5497     case ANALYSIS_TIMER_ID:\r
5498       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5499                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5500         AnalysisPeriodicEvent(0);\r
5501       } else {\r
5502         KillTimer(hwnd, analysisTimerEvent);\r
5503         analysisTimerEvent = 0;\r
5504       }\r
5505       break;\r
5506     case DELAYED_TIMER_ID:\r
5507       KillTimer(hwnd, delayedTimerEvent);\r
5508       delayedTimerEvent = 0;\r
5509       delayedTimerCallback();\r
5510       break;\r
5511     }\r
5512     break;\r
5513 \r
5514   case WM_USER_Input:\r
5515     InputEvent(hwnd, message, wParam, lParam);\r
5516     break;\r
5517 \r
5518   /* [AS] Also move "attached" child windows */\r
5519   case WM_WINDOWPOSCHANGING:\r
5520 \r
5521     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5522         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5523 \r
5524         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5525             /* Window is moving */\r
5526             RECT rcMain;\r
5527 \r
5528 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5529             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5530             rcMain.right  = wpMain.x + wpMain.width;\r
5531             rcMain.top    = wpMain.y;\r
5532             rcMain.bottom = wpMain.y + wpMain.height;\r
5533             \r
5534             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5535             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5536             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5537             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5538             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5539             wpMain.x = lpwp->x;\r
5540             wpMain.y = lpwp->y;\r
5541         }\r
5542     }\r
5543     break;\r
5544 \r
5545   /* [AS] Snapping */\r
5546   case WM_ENTERSIZEMOVE:\r
5547     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5548     if (hwnd == hwndMain) {\r
5549       doingSizing = TRUE;\r
5550       lastSizing = 0;\r
5551     }\r
5552     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5553     break;\r
5554 \r
5555   case WM_SIZING:\r
5556     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5557     if (hwnd == hwndMain) {\r
5558       lastSizing = wParam;\r
5559     }\r
5560     break;\r
5561 \r
5562   case WM_MOVING:\r
5563     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5564       return OnMoving( &sd, hwnd, wParam, lParam );\r
5565 \r
5566   case WM_EXITSIZEMOVE:\r
5567     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5568     if (hwnd == hwndMain) {\r
5569       RECT client;\r
5570       doingSizing = FALSE;\r
5571       InvalidateRect(hwnd, &boardRect, FALSE);\r
5572       GetClientRect(hwnd, &client);\r
5573       ResizeBoard(client.right, client.bottom, lastSizing);\r
5574       lastSizing = 0;\r
5575       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5576     }\r
5577     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5578     break;\r
5579 \r
5580   case WM_DESTROY: /* message: window being destroyed */\r
5581     PostQuitMessage(0);\r
5582     break;\r
5583 \r
5584   case WM_CLOSE:\r
5585     if (hwnd == hwndMain) {\r
5586       ExitEvent(0);\r
5587     }\r
5588     break;\r
5589 \r
5590   default:      /* Passes it on if unprocessed */\r
5591     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5592   }\r
5593   return 0;\r
5594 }\r
5595 \r
5596 /*---------------------------------------------------------------------------*\\r
5597  *\r
5598  * Misc utility routines\r
5599  *\r
5600 \*---------------------------------------------------------------------------*/\r
5601 \r
5602 /*\r
5603  * Decent random number generator, at least not as bad as Windows\r
5604  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5605  */\r
5606 unsigned int randstate;\r
5607 \r
5608 int\r
5609 myrandom(void)\r
5610 {\r
5611   randstate = randstate * 1664525 + 1013904223;\r
5612   return (int) randstate & 0x7fffffff;\r
5613 }\r
5614 \r
5615 void\r
5616 mysrandom(unsigned int seed)\r
5617 {\r
5618   randstate = seed;\r
5619 }\r
5620 \r
5621 \r
5622 /* \r
5623  * returns TRUE if user selects a different color, FALSE otherwise \r
5624  */\r
5625 \r
5626 BOOL\r
5627 ChangeColor(HWND hwnd, COLORREF *which)\r
5628 {\r
5629   static BOOL firstTime = TRUE;\r
5630   static DWORD customColors[16];\r
5631   CHOOSECOLOR cc;\r
5632   COLORREF newcolor;\r
5633   int i;\r
5634   ColorClass ccl;\r
5635 \r
5636   if (firstTime) {\r
5637     /* Make initial colors in use available as custom colors */\r
5638     /* Should we put the compiled-in defaults here instead? */\r
5639     i = 0;\r
5640     customColors[i++] = lightSquareColor & 0xffffff;\r
5641     customColors[i++] = darkSquareColor & 0xffffff;\r
5642     customColors[i++] = whitePieceColor & 0xffffff;\r
5643     customColors[i++] = blackPieceColor & 0xffffff;\r
5644     customColors[i++] = highlightSquareColor & 0xffffff;\r
5645     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5646 \r
5647     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5648       customColors[i++] = textAttribs[ccl].color;\r
5649     }\r
5650     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5651     firstTime = FALSE;\r
5652   }\r
5653 \r
5654   cc.lStructSize = sizeof(cc);\r
5655   cc.hwndOwner = hwnd;\r
5656   cc.hInstance = NULL;\r
5657   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5658   cc.lpCustColors = (LPDWORD) customColors;\r
5659   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5660 \r
5661   if (!ChooseColor(&cc)) return FALSE;\r
5662 \r
5663   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5664   if (newcolor == *which) return FALSE;\r
5665   *which = newcolor;\r
5666   return TRUE;\r
5667 \r
5668   /*\r
5669   InitDrawingColors();\r
5670   InvalidateRect(hwnd, &boardRect, FALSE);\r
5671   */\r
5672 }\r
5673 \r
5674 BOOLEAN\r
5675 MyLoadSound(MySound *ms)\r
5676 {\r
5677   BOOL ok = FALSE;\r
5678   struct stat st;\r
5679   FILE *f;\r
5680 \r
5681   if (ms->data && ms->flag) free(ms->data);\r
5682   ms->data = NULL;\r
5683 \r
5684   switch (ms->name[0]) {\r
5685   case NULLCHAR:\r
5686     /* Silence */\r
5687     ok = TRUE;\r
5688     break;\r
5689   case '$':\r
5690     /* System sound from Control Panel.  Don't preload here. */\r
5691     ok = TRUE;\r
5692     break;\r
5693   case '!':\r
5694     if (ms->name[1] == NULLCHAR) {\r
5695       /* "!" alone = silence */\r
5696       ok = TRUE;\r
5697     } else {\r
5698       /* Builtin wave resource.  Error if not found. */\r
5699       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5700       if (h == NULL) break;\r
5701       ms->data = (void *)LoadResource(hInst, h);\r
5702       ms->flag = 0; // not maloced, so cannot be freed!\r
5703       if (h == NULL) break;\r
5704       ok = TRUE;\r
5705     }\r
5706     break;\r
5707   default:\r
5708     /* .wav file.  Error if not found. */\r
5709     f = fopen(ms->name, "rb");\r
5710     if (f == NULL) break;\r
5711     if (fstat(fileno(f), &st) < 0) break;\r
5712     ms->data = malloc(st.st_size);\r
5713     ms->flag = 1;\r
5714     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5715     fclose(f);\r
5716     ok = TRUE;\r
5717     break;\r
5718   }\r
5719   if (!ok) {\r
5720     char buf[MSG_SIZ];\r
5721       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5722     DisplayError(buf, GetLastError());\r
5723   }\r
5724   return ok;\r
5725 }\r
5726 \r
5727 BOOLEAN\r
5728 MyPlaySound(MySound *ms)\r
5729 {\r
5730   BOOLEAN ok = FALSE;\r
5731 \r
5732   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5733   switch (ms->name[0]) {\r
5734   case NULLCHAR:\r
5735         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5736     /* Silence */\r
5737     ok = TRUE;\r
5738     break;\r
5739   case '$':\r
5740     /* System sound from Control Panel (deprecated feature).\r
5741        "$" alone or an unset sound name gets default beep (still in use). */\r
5742     if (ms->name[1]) {\r
5743       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5744     }\r
5745     if (!ok) ok = MessageBeep(MB_OK);\r
5746     break; \r
5747   case '!':\r
5748     /* Builtin wave resource, or "!" alone for silence */\r
5749     if (ms->name[1]) {\r
5750       if (ms->data == NULL) return FALSE;\r
5751       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5752     } else {\r
5753       ok = TRUE;\r
5754     }\r
5755     break;\r
5756   default:\r
5757     /* .wav file.  Error if not found. */\r
5758     if (ms->data == NULL) return FALSE;\r
5759     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5760     break;\r
5761   }\r
5762   /* Don't print an error: this can happen innocently if the sound driver\r
5763      is busy; for instance, if another instance of WinBoard is playing\r
5764      a sound at about the same time. */\r
5765   return ok;\r
5766 }\r
5767 \r
5768 \r
5769 LRESULT CALLBACK\r
5770 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5771 {\r
5772   BOOL ok;\r
5773   OPENFILENAME *ofn;\r
5774   static UINT *number; /* gross that this is static */\r
5775 \r
5776   switch (message) {\r
5777   case WM_INITDIALOG: /* message: initialize dialog box */\r
5778     /* Center the dialog over the application window */\r
5779     ofn = (OPENFILENAME *) lParam;\r
5780     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5781       number = (UINT *) ofn->lCustData;\r
5782       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5783     } else {\r
5784       number = NULL;\r
5785     }\r
5786     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5787     Translate(hDlg, 1536);\r
5788     return FALSE;  /* Allow for further processing */\r
5789 \r
5790   case WM_COMMAND:\r
5791     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5792       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5793     }\r
5794     return FALSE;  /* Allow for further processing */\r
5795   }\r
5796   return FALSE;\r
5797 }\r
5798 \r
5799 UINT APIENTRY\r
5800 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5801 {\r
5802   static UINT *number;\r
5803   OPENFILENAME *ofname;\r
5804   OFNOTIFY *ofnot;\r
5805   switch (uiMsg) {\r
5806   case WM_INITDIALOG:\r
5807     Translate(hdlg, DLG_IndexNumber);\r
5808     ofname = (OPENFILENAME *)lParam;\r
5809     number = (UINT *)(ofname->lCustData);\r
5810     break;\r
5811   case WM_NOTIFY:\r
5812     ofnot = (OFNOTIFY *)lParam;\r
5813     if (ofnot->hdr.code == CDN_FILEOK) {\r
5814       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5815     }\r
5816     break;\r
5817   }\r
5818   return 0;\r
5819 }\r
5820 \r
5821 \r
5822 FILE *\r
5823 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5824                char *nameFilt, char *dlgTitle, UINT *number,\r
5825                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5826 {\r
5827   OPENFILENAME openFileName;\r
5828   char buf1[MSG_SIZ];\r
5829   FILE *f;\r
5830 \r
5831   if (fileName == NULL) fileName = buf1;\r
5832   if (defName == NULL) {\r
5833     safeStrCpy(fileName, "*.", 3 );\r
5834     strcat(fileName, defExt);\r
5835   } else {\r
5836     safeStrCpy(fileName, defName, MSG_SIZ );\r
5837   }\r
5838     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5839   if (number) *number = 0;\r
5840 \r
5841   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5842   openFileName.hwndOwner         = hwnd;\r
5843   openFileName.hInstance         = (HANDLE) hInst;\r
5844   openFileName.lpstrFilter       = nameFilt;\r
5845   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5846   openFileName.nMaxCustFilter    = 0L;\r
5847   openFileName.nFilterIndex      = 1L;\r
5848   openFileName.lpstrFile         = fileName;\r
5849   openFileName.nMaxFile          = MSG_SIZ;\r
5850   openFileName.lpstrFileTitle    = fileTitle;\r
5851   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5852   openFileName.lpstrInitialDir   = NULL;\r
5853   openFileName.lpstrTitle        = dlgTitle;\r
5854   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5855     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5856     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5857     | (oldDialog ? 0 : OFN_EXPLORER);\r
5858   openFileName.nFileOffset       = 0;\r
5859   openFileName.nFileExtension    = 0;\r
5860   openFileName.lpstrDefExt       = defExt;\r
5861   openFileName.lCustData         = (LONG) number;\r
5862   openFileName.lpfnHook          = oldDialog ?\r
5863     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5864   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5865 \r
5866   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5867                         GetOpenFileName(&openFileName)) {\r
5868     /* open the file */\r
5869     f = fopen(openFileName.lpstrFile, write);\r
5870     if (f == NULL) {\r
5871       MessageBox(hwnd, _("File open failed"), NULL,\r
5872                  MB_OK|MB_ICONEXCLAMATION);\r
5873       return NULL;\r
5874     }\r
5875   } else {\r
5876     int err = CommDlgExtendedError();\r
5877     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5878     return FALSE;\r
5879   }\r
5880   return f;\r
5881 }\r
5882 \r
5883 \r
5884 \r
5885 VOID APIENTRY\r
5886 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5887 {\r
5888   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5889 \r
5890   /*\r
5891    * Get the first pop-up menu in the menu template. This is the\r
5892    * menu that TrackPopupMenu displays.\r
5893    */\r
5894   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5895   TranslateOneMenu(10, hmenuTrackPopup);\r
5896 \r
5897   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5898 \r
5899   /*\r
5900    * TrackPopup uses screen coordinates, so convert the\r
5901    * coordinates of the mouse click to screen coordinates.\r
5902    */\r
5903   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5904 \r
5905   /* Draw and track the floating pop-up menu. */\r
5906   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5907                  pt.x, pt.y, 0, hwnd, NULL);\r
5908 \r
5909   /* Destroy the menu.*/\r
5910   DestroyMenu(hmenu);\r
5911 }\r
5912    \r
5913 typedef struct {\r
5914   HWND hDlg, hText;\r
5915   int sizeX, sizeY, newSizeX, newSizeY;\r
5916   HDWP hdwp;\r
5917 } ResizeEditPlusButtonsClosure;\r
5918 \r
5919 BOOL CALLBACK\r
5920 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5921 {\r
5922   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5923   RECT rect;\r
5924   POINT pt;\r
5925 \r
5926   if (hChild == cl->hText) return TRUE;\r
5927   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5928   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5929   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5930   ScreenToClient(cl->hDlg, &pt);\r
5931   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5932     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5933   return TRUE;\r
5934 }\r
5935 \r
5936 /* Resize a dialog that has a (rich) edit field filling most of\r
5937    the top, with a row of buttons below */\r
5938 VOID\r
5939 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5940 {\r
5941   RECT rectText;\r
5942   int newTextHeight, newTextWidth;\r
5943   ResizeEditPlusButtonsClosure cl;\r
5944   \r
5945   /*if (IsIconic(hDlg)) return;*/\r
5946   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5947   \r
5948   cl.hdwp = BeginDeferWindowPos(8);\r
5949 \r
5950   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5951   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5952   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5953   if (newTextHeight < 0) {\r
5954     newSizeY += -newTextHeight;\r
5955     newTextHeight = 0;\r
5956   }\r
5957   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5958     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5959 \r
5960   cl.hDlg = hDlg;\r
5961   cl.hText = hText;\r
5962   cl.sizeX = sizeX;\r
5963   cl.sizeY = sizeY;\r
5964   cl.newSizeX = newSizeX;\r
5965   cl.newSizeY = newSizeY;\r
5966   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5967 \r
5968   EndDeferWindowPos(cl.hdwp);\r
5969 }\r
5970 \r
5971 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5972 {\r
5973     RECT    rChild, rParent;\r
5974     int     wChild, hChild, wParent, hParent;\r
5975     int     wScreen, hScreen, xNew, yNew;\r
5976     HDC     hdc;\r
5977 \r
5978     /* Get the Height and Width of the child window */\r
5979     GetWindowRect (hwndChild, &rChild);\r
5980     wChild = rChild.right - rChild.left;\r
5981     hChild = rChild.bottom - rChild.top;\r
5982 \r
5983     /* Get the Height and Width of the parent window */\r
5984     GetWindowRect (hwndParent, &rParent);\r
5985     wParent = rParent.right - rParent.left;\r
5986     hParent = rParent.bottom - rParent.top;\r
5987 \r
5988     /* Get the display limits */\r
5989     hdc = GetDC (hwndChild);\r
5990     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5991     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5992     ReleaseDC(hwndChild, hdc);\r
5993 \r
5994     /* Calculate new X position, then adjust for screen */\r
5995     xNew = rParent.left + ((wParent - wChild) /2);\r
5996     if (xNew < 0) {\r
5997         xNew = 0;\r
5998     } else if ((xNew+wChild) > wScreen) {\r
5999         xNew = wScreen - wChild;\r
6000     }\r
6001 \r
6002     /* Calculate new Y position, then adjust for screen */\r
6003     if( mode == 0 ) {\r
6004         yNew = rParent.top  + ((hParent - hChild) /2);\r
6005     }\r
6006     else {\r
6007         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6008     }\r
6009 \r
6010     if (yNew < 0) {\r
6011         yNew = 0;\r
6012     } else if ((yNew+hChild) > hScreen) {\r
6013         yNew = hScreen - hChild;\r
6014     }\r
6015 \r
6016     /* Set it, and return */\r
6017     return SetWindowPos (hwndChild, NULL,\r
6018                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6019 }\r
6020 \r
6021 /* Center one window over another */\r
6022 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6023 {\r
6024     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6025 }\r
6026 \r
6027 /*---------------------------------------------------------------------------*\\r
6028  *\r
6029  * Startup Dialog functions\r
6030  *\r
6031 \*---------------------------------------------------------------------------*/\r
6032 void\r
6033 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6034 {\r
6035   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6036 \r
6037   while (*cd != NULL) {\r
6038     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6039     cd++;\r
6040   }\r
6041 }\r
6042 \r
6043 void\r
6044 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6045 {\r
6046   char buf1[MAX_ARG_LEN];\r
6047   int len;\r
6048 \r
6049   if (str[0] == '@') {\r
6050     FILE* f = fopen(str + 1, "r");\r
6051     if (f == NULL) {\r
6052       DisplayFatalError(str + 1, errno, 2);\r
6053       return;\r
6054     }\r
6055     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6056     fclose(f);\r
6057     buf1[len] = NULLCHAR;\r
6058     str = buf1;\r
6059   }\r
6060 \r
6061   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6062 \r
6063   for (;;) {\r
6064     char buf[MSG_SIZ];\r
6065     char *end = strchr(str, '\n');\r
6066     if (end == NULL) return;\r
6067     memcpy(buf, str, end - str);\r
6068     buf[end - str] = NULLCHAR;\r
6069     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6070     str = end + 1;\r
6071   }\r
6072 }\r
6073 \r
6074 void\r
6075 SetStartupDialogEnables(HWND hDlg)\r
6076 {\r
6077   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6078     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6079     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6080   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6081     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6082   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6083     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6084   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6085     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6086   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6087     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6088     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6089     IsDlgButtonChecked(hDlg, OPT_View));\r
6090 }\r
6091 \r
6092 char *\r
6093 QuoteForFilename(char *filename)\r
6094 {\r
6095   int dquote, space;\r
6096   dquote = strchr(filename, '"') != NULL;\r
6097   space = strchr(filename, ' ') != NULL;\r
6098   if (dquote || space) {\r
6099     if (dquote) {\r
6100       return "'";\r
6101     } else {\r
6102       return "\"";\r
6103     }\r
6104   } else {\r
6105     return "";\r
6106   }\r
6107 }\r
6108 \r
6109 VOID\r
6110 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6111 {\r
6112   char buf[MSG_SIZ];\r
6113   char *q;\r
6114 \r
6115   InitComboStringsFromOption(hwndCombo, nthnames);\r
6116   q = QuoteForFilename(nthcp);\r
6117     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6118   if (*nthdir != NULLCHAR) {\r
6119     q = QuoteForFilename(nthdir);\r
6120       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6121   }\r
6122   if (*nthcp == NULLCHAR) {\r
6123     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6124   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6125     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6126     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6127   }\r
6128 }\r
6129 \r
6130 LRESULT CALLBACK\r
6131 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6132 {\r
6133   char buf[MSG_SIZ];\r
6134   HANDLE hwndCombo;\r
6135   char *p;\r
6136 \r
6137   switch (message) {\r
6138   case WM_INITDIALOG:\r
6139     /* Center the dialog */\r
6140     CenterWindow (hDlg, GetDesktopWindow());\r
6141     Translate(hDlg, DLG_Startup);\r
6142     /* Initialize the dialog items */\r
6143     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6144                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6145                   firstChessProgramNames);\r
6146     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6147                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6148                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6149     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6150     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6151       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6152     if (*appData.icsHelper != NULLCHAR) {\r
6153       char *q = QuoteForFilename(appData.icsHelper);\r
6154       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6155     }\r
6156     if (*appData.icsHost == NULLCHAR) {\r
6157       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6158       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6159     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6160       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6161       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6162     }\r
6163 \r
6164     if (appData.icsActive) {\r
6165       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6166     }\r
6167     else if (appData.noChessProgram) {\r
6168       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6169     }\r
6170     else {\r
6171       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6172     }\r
6173 \r
6174     SetStartupDialogEnables(hDlg);\r
6175     return TRUE;\r
6176 \r
6177   case WM_COMMAND:\r
6178     switch (LOWORD(wParam)) {\r
6179     case IDOK:\r
6180       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6181         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6182         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6183         p = buf;\r
6184         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6185         ParseArgs(StringGet, &p);\r
6186         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6187         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6188         p = buf;\r
6189         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6190         ParseArgs(StringGet, &p);\r
6191         SwapEngines(singleList); // ... and then make it 'second'\r
6192         appData.noChessProgram = FALSE;\r
6193         appData.icsActive = FALSE;\r
6194       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6195         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6196         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6197         p = buf;\r
6198         ParseArgs(StringGet, &p);\r
6199         if (appData.zippyPlay) {\r
6200           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6201           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6202           p = buf;\r
6203           ParseArgs(StringGet, &p);\r
6204         }\r
6205       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6206         appData.noChessProgram = TRUE;\r
6207         appData.icsActive = FALSE;\r
6208       } else {\r
6209         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6210                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6211         return TRUE;\r
6212       }\r
6213       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6214         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6215         p = buf;\r
6216         ParseArgs(StringGet, &p);\r
6217       }\r
6218       EndDialog(hDlg, TRUE);\r
6219       return TRUE;\r
6220 \r
6221     case IDCANCEL:\r
6222       ExitEvent(0);\r
6223       return TRUE;\r
6224 \r
6225     case IDM_HELPCONTENTS:\r
6226       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6227         MessageBox (GetFocus(),\r
6228                     _("Unable to activate help"),\r
6229                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6230       }\r
6231       break;\r
6232 \r
6233     default:\r
6234       SetStartupDialogEnables(hDlg);\r
6235       break;\r
6236     }\r
6237     break;\r
6238   }\r
6239   return FALSE;\r
6240 }\r
6241 \r
6242 /*---------------------------------------------------------------------------*\\r
6243  *\r
6244  * About box dialog functions\r
6245  *\r
6246 \*---------------------------------------------------------------------------*/\r
6247 \r
6248 /* Process messages for "About" dialog box */\r
6249 LRESULT CALLBACK\r
6250 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6251 {\r
6252   switch (message) {\r
6253   case WM_INITDIALOG: /* message: initialize dialog box */\r
6254     /* Center the dialog over the application window */\r
6255     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6256     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6257     Translate(hDlg, ABOUTBOX);\r
6258     JAWS_COPYRIGHT\r
6259     return (TRUE);\r
6260 \r
6261   case WM_COMMAND: /* message: received a command */\r
6262     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6263         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6264       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6265       return (TRUE);\r
6266     }\r
6267     break;\r
6268   }\r
6269   return (FALSE);\r
6270 }\r
6271 \r
6272 /*---------------------------------------------------------------------------*\\r
6273  *\r
6274  * Comment Dialog functions\r
6275  *\r
6276 \*---------------------------------------------------------------------------*/\r
6277 \r
6278 LRESULT CALLBACK\r
6279 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6280 {\r
6281   static HANDLE hwndText = NULL;\r
6282   int len, newSizeX, newSizeY, flags;\r
6283   static int sizeX, sizeY;\r
6284   char *str;\r
6285   RECT rect;\r
6286   MINMAXINFO *mmi;\r
6287 \r
6288   switch (message) {\r
6289   case WM_INITDIALOG: /* message: initialize dialog box */\r
6290     /* Initialize the dialog items */\r
6291     Translate(hDlg, DLG_EditComment);\r
6292     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6293     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6294     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6295     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6296     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6297     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6298     SetWindowText(hDlg, commentTitle);\r
6299     if (editComment) {\r
6300       SetFocus(hwndText);\r
6301     } else {\r
6302       SetFocus(GetDlgItem(hDlg, IDOK));\r
6303     }\r
6304     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6305                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6306                 MAKELPARAM(FALSE, 0));\r
6307     /* Size and position the dialog */\r
6308     if (!commentDialog) {\r
6309       commentDialog = hDlg;\r
6310       flags = SWP_NOZORDER;\r
6311       GetClientRect(hDlg, &rect);\r
6312       sizeX = rect.right;\r
6313       sizeY = rect.bottom;\r
6314       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6315           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6316         WINDOWPLACEMENT wp;\r
6317         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6318         wp.length = sizeof(WINDOWPLACEMENT);\r
6319         wp.flags = 0;\r
6320         wp.showCmd = SW_SHOW;\r
6321         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6322         wp.rcNormalPosition.left = wpComment.x;\r
6323         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6324         wp.rcNormalPosition.top = wpComment.y;\r
6325         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6326         SetWindowPlacement(hDlg, &wp);\r
6327 \r
6328         GetClientRect(hDlg, &rect);\r
6329         newSizeX = rect.right;\r
6330         newSizeY = rect.bottom;\r
6331         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6332                               newSizeX, newSizeY);\r
6333         sizeX = newSizeX;\r
6334         sizeY = newSizeY;\r
6335       }\r
6336     }\r
6337     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6338     return FALSE;\r
6339 \r
6340   case WM_COMMAND: /* message: received a command */\r
6341     switch (LOWORD(wParam)) {\r
6342     case IDOK:\r
6343       if (editComment) {\r
6344         char *p, *q;\r
6345         /* Read changed options from the dialog box */\r
6346         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6347         len = GetWindowTextLength(hwndText);\r
6348         str = (char *) malloc(len + 1);\r
6349         GetWindowText(hwndText, str, len + 1);\r
6350         p = q = str;\r
6351         while (*q) {\r
6352           if (*q == '\r')\r
6353             q++;\r
6354           else\r
6355             *p++ = *q++;\r
6356         }\r
6357         *p = NULLCHAR;\r
6358         ReplaceComment(commentIndex, str);\r
6359         free(str);\r
6360       }\r
6361       CommentPopDown();\r
6362       return TRUE;\r
6363 \r
6364     case IDCANCEL:\r
6365     case OPT_CancelComment:\r
6366       CommentPopDown();\r
6367       return TRUE;\r
6368 \r
6369     case OPT_ClearComment:\r
6370       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6371       break;\r
6372 \r
6373     case OPT_EditComment:\r
6374       EditCommentEvent();\r
6375       return TRUE;\r
6376 \r
6377     default:\r
6378       break;\r
6379     }\r
6380     break;\r
6381 \r
6382   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6383         if( wParam == OPT_CommentText ) {\r
6384             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6385 \r
6386             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6387                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6388                 POINTL pt;\r
6389                 LRESULT index;\r
6390 \r
6391                 pt.x = LOWORD( lpMF->lParam );\r
6392                 pt.y = HIWORD( lpMF->lParam );\r
6393 \r
6394                 if(lpMF->msg == WM_CHAR) {\r
6395                         CHARRANGE sel;\r
6396                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6397                         index = sel.cpMin;\r
6398                 } else\r
6399                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6400 \r
6401                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6402                 len = GetWindowTextLength(hwndText);\r
6403                 str = (char *) malloc(len + 1);\r
6404                 GetWindowText(hwndText, str, len + 1);\r
6405                 ReplaceComment(commentIndex, str);\r
6406                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6407                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6408                 free(str);\r
6409 \r
6410                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6411                 lpMF->msg = WM_USER;\r
6412 \r
6413                 return TRUE;\r
6414             }\r
6415         }\r
6416         break;\r
6417 \r
6418   case WM_SIZE:\r
6419     newSizeX = LOWORD(lParam);\r
6420     newSizeY = HIWORD(lParam);\r
6421     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6422     sizeX = newSizeX;\r
6423     sizeY = newSizeY;\r
6424     break;\r
6425 \r
6426   case WM_GETMINMAXINFO:\r
6427     /* Prevent resizing window too small */\r
6428     mmi = (MINMAXINFO *) lParam;\r
6429     mmi->ptMinTrackSize.x = 100;\r
6430     mmi->ptMinTrackSize.y = 100;\r
6431     break;\r
6432   }\r
6433   return FALSE;\r
6434 }\r
6435 \r
6436 VOID\r
6437 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6438 {\r
6439   FARPROC lpProc;\r
6440   char *p, *q;\r
6441 \r
6442   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6443 \r
6444   if (str == NULL) str = "";\r
6445   p = (char *) malloc(2 * strlen(str) + 2);\r
6446   q = p;\r
6447   while (*str) {\r
6448     if (*str == '\n') *q++ = '\r';\r
6449     *q++ = *str++;\r
6450   }\r
6451   *q = NULLCHAR;\r
6452   if (commentText != NULL) free(commentText);\r
6453 \r
6454   commentIndex = index;\r
6455   commentTitle = title;\r
6456   commentText = p;\r
6457   editComment = edit;\r
6458 \r
6459   if (commentDialog) {\r
6460     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6461     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6462   } else {\r
6463     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6464     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6465                  hwndMain, (DLGPROC)lpProc);\r
6466     FreeProcInstance(lpProc);\r
6467   }\r
6468   commentUp = TRUE;\r
6469 }\r
6470 \r
6471 \r
6472 /*---------------------------------------------------------------------------*\\r
6473  *\r
6474  * Type-in move dialog functions\r
6475  * \r
6476 \*---------------------------------------------------------------------------*/\r
6477 \r
6478 LRESULT CALLBACK\r
6479 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6480 {\r
6481   char move[MSG_SIZ];\r
6482   HWND hInput;\r
6483 \r
6484   switch (message) {\r
6485   case WM_INITDIALOG:\r
6486     move[0] = (char) lParam;\r
6487     move[1] = NULLCHAR;\r
6488     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6489     Translate(hDlg, DLG_TypeInMove);\r
6490     hInput = GetDlgItem(hDlg, OPT_Move);\r
6491     SetWindowText(hInput, move);\r
6492     SetFocus(hInput);\r
6493     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6494     return FALSE;\r
6495 \r
6496   case WM_COMMAND:\r
6497     switch (LOWORD(wParam)) {\r
6498     case IDOK:\r
6499 \r
6500       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6501       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6502       TypeInDoneEvent(move);\r
6503       EndDialog(hDlg, TRUE);\r
6504       return TRUE;\r
6505     case IDCANCEL:\r
6506       EndDialog(hDlg, FALSE);\r
6507       return TRUE;\r
6508     default:\r
6509       break;\r
6510     }\r
6511     break;\r
6512   }\r
6513   return FALSE;\r
6514 }\r
6515 \r
6516 VOID\r
6517 PopUpMoveDialog(char firstchar)\r
6518 {\r
6519     FARPROC lpProc;\r
6520 \r
6521       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6522       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6523         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6524       FreeProcInstance(lpProc);\r
6525 }\r
6526 \r
6527 /*---------------------------------------------------------------------------*\\r
6528  *\r
6529  * Type-in name dialog functions\r
6530  * \r
6531 \*---------------------------------------------------------------------------*/\r
6532 \r
6533 LRESULT CALLBACK\r
6534 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6535 {\r
6536   char move[MSG_SIZ];\r
6537   HWND hInput;\r
6538 \r
6539   switch (message) {\r
6540   case WM_INITDIALOG:\r
6541     move[0] = (char) lParam;\r
6542     move[1] = NULLCHAR;\r
6543     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6544     Translate(hDlg, DLG_TypeInName);\r
6545     hInput = GetDlgItem(hDlg, OPT_Name);\r
6546     SetWindowText(hInput, move);\r
6547     SetFocus(hInput);\r
6548     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6549     return FALSE;\r
6550 \r
6551   case WM_COMMAND:\r
6552     switch (LOWORD(wParam)) {\r
6553     case IDOK:\r
6554       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6555       appData.userName = strdup(move);\r
6556       SetUserLogo();\r
6557       SetGameInfo();\r
6558       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6559         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6560         DisplayTitle(move);\r
6561       }\r
6562 \r
6563 \r
6564       EndDialog(hDlg, TRUE);\r
6565       return TRUE;\r
6566     case IDCANCEL:\r
6567       EndDialog(hDlg, FALSE);\r
6568       return TRUE;\r
6569     default:\r
6570       break;\r
6571     }\r
6572     break;\r
6573   }\r
6574   return FALSE;\r
6575 }\r
6576 \r
6577 VOID\r
6578 PopUpNameDialog(char firstchar)\r
6579 {\r
6580     FARPROC lpProc;\r
6581     \r
6582       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6583       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6584         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6585       FreeProcInstance(lpProc);\r
6586 }\r
6587 \r
6588 /*---------------------------------------------------------------------------*\\r
6589  *\r
6590  *  Error dialogs\r
6591  * \r
6592 \*---------------------------------------------------------------------------*/\r
6593 \r
6594 /* Nonmodal error box */\r
6595 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6596                              WPARAM wParam, LPARAM lParam);\r
6597 \r
6598 VOID\r
6599 ErrorPopUp(char *title, char *content)\r
6600 {\r
6601   FARPROC lpProc;\r
6602   char *p, *q;\r
6603   BOOLEAN modal = hwndMain == NULL;\r
6604 \r
6605   p = content;\r
6606   q = errorMessage;\r
6607   while (*p) {\r
6608     if (*p == '\n') {\r
6609       if (modal) {\r
6610         *q++ = ' ';\r
6611         p++;\r
6612       } else {\r
6613         *q++ = '\r';\r
6614         *q++ = *p++;\r
6615       }\r
6616     } else {\r
6617       *q++ = *p++;\r
6618     }\r
6619   }\r
6620   *q = NULLCHAR;\r
6621   strncpy(errorTitle, title, sizeof(errorTitle));\r
6622   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6623   \r
6624   if (modal) {\r
6625     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6626   } else {\r
6627     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6628     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6629                  hwndMain, (DLGPROC)lpProc);\r
6630     FreeProcInstance(lpProc);\r
6631   }\r
6632 }\r
6633 \r
6634 VOID\r
6635 ErrorPopDown()\r
6636 {\r
6637   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6638   if (errorDialog == NULL) return;\r
6639   DestroyWindow(errorDialog);\r
6640   errorDialog = NULL;\r
6641   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6642 }\r
6643 \r
6644 LRESULT CALLBACK\r
6645 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6646 {\r
6647   HANDLE hwndText;\r
6648   RECT rChild;\r
6649 \r
6650   switch (message) {\r
6651   case WM_INITDIALOG:\r
6652     GetWindowRect(hDlg, &rChild);\r
6653 \r
6654     /*\r
6655     SetWindowPos(hDlg, NULL, rChild.left,\r
6656       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6657       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6658     */\r
6659 \r
6660     /* \r
6661         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6662         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6663         and it doesn't work when you resize the dialog.\r
6664         For now, just give it a default position.\r
6665     */\r
6666     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6667     Translate(hDlg, DLG_Error);\r
6668 \r
6669     errorDialog = hDlg;\r
6670     SetWindowText(hDlg, errorTitle);\r
6671     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6672     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6673     return FALSE;\r
6674 \r
6675   case WM_COMMAND:\r
6676     switch (LOWORD(wParam)) {\r
6677     case IDOK:\r
6678     case IDCANCEL:\r
6679       if (errorDialog == hDlg) errorDialog = NULL;\r
6680       DestroyWindow(hDlg);\r
6681       return TRUE;\r
6682 \r
6683     default:\r
6684       break;\r
6685     }\r
6686     break;\r
6687   }\r
6688   return FALSE;\r
6689 }\r
6690 \r
6691 #ifdef GOTHIC\r
6692 HWND gothicDialog = NULL;\r
6693 \r
6694 LRESULT CALLBACK\r
6695 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6696 {\r
6697   HANDLE hwndText;\r
6698   RECT rChild;\r
6699   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6700 \r
6701   switch (message) {\r
6702   case WM_INITDIALOG:\r
6703     GetWindowRect(hDlg, &rChild);\r
6704 \r
6705     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6706                                                              SWP_NOZORDER);\r
6707 \r
6708     /* \r
6709         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6710         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6711         and it doesn't work when you resize the dialog.\r
6712         For now, just give it a default position.\r
6713     */\r
6714     gothicDialog = hDlg;\r
6715     SetWindowText(hDlg, errorTitle);\r
6716     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6717     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6718     return FALSE;\r
6719 \r
6720   case WM_COMMAND:\r
6721     switch (LOWORD(wParam)) {\r
6722     case IDOK:\r
6723     case IDCANCEL:\r
6724       if (errorDialog == hDlg) errorDialog = NULL;\r
6725       DestroyWindow(hDlg);\r
6726       return TRUE;\r
6727 \r
6728     default:\r
6729       break;\r
6730     }\r
6731     break;\r
6732   }\r
6733   return FALSE;\r
6734 }\r
6735 \r
6736 VOID\r
6737 GothicPopUp(char *title, VariantClass variant)\r
6738 {\r
6739   FARPROC lpProc;\r
6740   static char *lastTitle;\r
6741 \r
6742   strncpy(errorTitle, title, sizeof(errorTitle));\r
6743   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6744 \r
6745   if(lastTitle != title && gothicDialog != NULL) {\r
6746     DestroyWindow(gothicDialog);\r
6747     gothicDialog = NULL;\r
6748   }\r
6749   if(variant != VariantNormal && gothicDialog == NULL) {\r
6750     title = lastTitle;\r
6751     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6752     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6753                  hwndMain, (DLGPROC)lpProc);\r
6754     FreeProcInstance(lpProc);\r
6755   }\r
6756 }\r
6757 #endif\r
6758 \r
6759 /*---------------------------------------------------------------------------*\\r
6760  *\r
6761  *  Ics Interaction console functions\r
6762  *\r
6763 \*---------------------------------------------------------------------------*/\r
6764 \r
6765 #define HISTORY_SIZE 64\r
6766 static char *history[HISTORY_SIZE];\r
6767 int histIn = 0, histP = 0;\r
6768 \r
6769 VOID\r
6770 SaveInHistory(char *cmd)\r
6771 {\r
6772   if (history[histIn] != NULL) {\r
6773     free(history[histIn]);\r
6774     history[histIn] = NULL;\r
6775   }\r
6776   if (*cmd == NULLCHAR) return;\r
6777   history[histIn] = StrSave(cmd);\r
6778   histIn = (histIn + 1) % HISTORY_SIZE;\r
6779   if (history[histIn] != NULL) {\r
6780     free(history[histIn]);\r
6781     history[histIn] = NULL;\r
6782   }\r
6783   histP = histIn;\r
6784 }\r
6785 \r
6786 char *\r
6787 PrevInHistory(char *cmd)\r
6788 {\r
6789   int newhp;\r
6790   if (histP == histIn) {\r
6791     if (history[histIn] != NULL) free(history[histIn]);\r
6792     history[histIn] = StrSave(cmd);\r
6793   }\r
6794   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6795   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6796   histP = newhp;\r
6797   return history[histP];\r
6798 }\r
6799 \r
6800 char *\r
6801 NextInHistory()\r
6802 {\r
6803   if (histP == histIn) return NULL;\r
6804   histP = (histP + 1) % HISTORY_SIZE;\r
6805   return history[histP];   \r
6806 }\r
6807 \r
6808 HMENU\r
6809 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6810 {\r
6811   HMENU hmenu, h;\r
6812   int i = 0;\r
6813   hmenu = LoadMenu(hInst, "TextMenu");\r
6814   h = GetSubMenu(hmenu, 0);\r
6815   while (e->item) {\r
6816     if (strcmp(e->item, "-") == 0) {\r
6817       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6818     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6819       int flags = MF_STRING, j = 0;\r
6820       if (e->item[0] == '|') {\r
6821         flags |= MF_MENUBARBREAK;\r
6822         j++;\r
6823       }\r
6824       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6825       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6826     }\r
6827     e++;\r
6828     i++;\r
6829   } \r
6830   return hmenu;\r
6831 }\r
6832 \r
6833 WNDPROC consoleTextWindowProc;\r
6834 \r
6835 void\r
6836 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6837 {\r
6838   char buf[MSG_SIZ], name[MSG_SIZ];\r
6839   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6840   CHARRANGE sel;\r
6841 \r
6842   if (!getname) {\r
6843     SetWindowText(hInput, command);\r
6844     if (immediate) {\r
6845       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6846     } else {\r
6847       sel.cpMin = 999999;\r
6848       sel.cpMax = 999999;\r
6849       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6850       SetFocus(hInput);\r
6851     }\r
6852     return;\r
6853   }    \r
6854   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6855   if (sel.cpMin == sel.cpMax) {\r
6856     /* Expand to surrounding word */\r
6857     TEXTRANGE tr;\r
6858     do {\r
6859       tr.chrg.cpMax = sel.cpMin;\r
6860       tr.chrg.cpMin = --sel.cpMin;\r
6861       if (sel.cpMin < 0) break;\r
6862       tr.lpstrText = name;\r
6863       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6864     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6865     sel.cpMin++;\r
6866 \r
6867     do {\r
6868       tr.chrg.cpMin = sel.cpMax;\r
6869       tr.chrg.cpMax = ++sel.cpMax;\r
6870       tr.lpstrText = name;\r
6871       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6872     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6873     sel.cpMax--;\r
6874 \r
6875     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6876       MessageBeep(MB_ICONEXCLAMATION);\r
6877       return;\r
6878     }\r
6879     tr.chrg = sel;\r
6880     tr.lpstrText = name;\r
6881     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6882   } else {\r
6883     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6884       MessageBeep(MB_ICONEXCLAMATION);\r
6885       return;\r
6886     }\r
6887     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6888   }\r
6889   if (immediate) {\r
6890     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6891     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6892     SetWindowText(hInput, buf);\r
6893     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6894   } else {\r
6895     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6896       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6897     SetWindowText(hInput, buf);\r
6898     sel.cpMin = 999999;\r
6899     sel.cpMax = 999999;\r
6900     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6901     SetFocus(hInput);\r
6902   }\r
6903 }\r
6904 \r
6905 LRESULT CALLBACK \r
6906 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6907 {\r
6908   HWND hInput;\r
6909   CHARRANGE sel;\r
6910 \r
6911   switch (message) {\r
6912   case WM_KEYDOWN:\r
6913     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6914     if(wParam=='R') return 0;\r
6915     switch (wParam) {\r
6916     case VK_PRIOR:\r
6917       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6918       return 0;\r
6919     case VK_NEXT:\r
6920       sel.cpMin = 999999;\r
6921       sel.cpMax = 999999;\r
6922       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6923       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6924       return 0;\r
6925     }\r
6926     break;\r
6927   case WM_CHAR:\r
6928    if(wParam != '\022') {\r
6929     if (wParam == '\t') {\r
6930       if (GetKeyState(VK_SHIFT) < 0) {\r
6931         /* shifted */\r
6932         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6933         if (buttonDesc[0].hwnd) {\r
6934           SetFocus(buttonDesc[0].hwnd);\r
6935         } else {\r
6936           SetFocus(hwndMain);\r
6937         }\r
6938       } else {\r
6939         /* unshifted */\r
6940         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6941       }\r
6942     } else {\r
6943       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6944       JAWS_DELETE( SetFocus(hInput); )\r
6945       SendMessage(hInput, message, wParam, lParam);\r
6946     }\r
6947     return 0;\r
6948    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6949    lParam = -1;\r
6950   case WM_RBUTTONDOWN:\r
6951     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6952       /* Move selection here if it was empty */\r
6953       POINT pt;\r
6954       pt.x = LOWORD(lParam);\r
6955       pt.y = HIWORD(lParam);\r
6956       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6957       if (sel.cpMin == sel.cpMax) {\r
6958         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6959         sel.cpMax = sel.cpMin;\r
6960         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6961       }\r
6962       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6963 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6964       POINT pt;\r
6965       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6966       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6967       if (sel.cpMin == sel.cpMax) {\r
6968         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6969         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6970       }\r
6971       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6972         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6973       }\r
6974       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6975       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6976       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6977       MenuPopup(hwnd, pt, hmenu, -1);\r
6978 }\r
6979     }\r
6980     return 0;\r
6981   case WM_RBUTTONUP:\r
6982     if (GetKeyState(VK_SHIFT) & ~1) {\r
6983       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6984         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6985     }\r
6986     return 0;\r
6987   case WM_PASTE:\r
6988     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6989     SetFocus(hInput);\r
6990     return SendMessage(hInput, message, wParam, lParam);\r
6991   case WM_MBUTTONDOWN:\r
6992     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6993   case WM_COMMAND:\r
6994     switch (LOWORD(wParam)) {\r
6995     case IDM_QuickPaste:\r
6996       {\r
6997         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6998         if (sel.cpMin == sel.cpMax) {\r
6999           MessageBeep(MB_ICONEXCLAMATION);\r
7000           return 0;\r
7001         }\r
7002         SendMessage(hwnd, WM_COPY, 0, 0);\r
7003         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7004         SendMessage(hInput, WM_PASTE, 0, 0);\r
7005         SetFocus(hInput);\r
7006         return 0;\r
7007       }\r
7008     case IDM_Cut:\r
7009       SendMessage(hwnd, WM_CUT, 0, 0);\r
7010       return 0;\r
7011     case IDM_Paste:\r
7012       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7013       return 0;\r
7014     case IDM_Copy:\r
7015       SendMessage(hwnd, WM_COPY, 0, 0);\r
7016       return 0;\r
7017     default:\r
7018       {\r
7019         int i = LOWORD(wParam) - IDM_CommandX;\r
7020         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7021             icsTextMenuEntry[i].command != NULL) {\r
7022           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7023                    icsTextMenuEntry[i].getname,\r
7024                    icsTextMenuEntry[i].immediate);\r
7025           return 0;\r
7026         }\r
7027       }\r
7028       break;\r
7029     }\r
7030     break;\r
7031   }\r
7032   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7033 }\r
7034 \r
7035 WNDPROC consoleInputWindowProc;\r
7036 \r
7037 LRESULT CALLBACK\r
7038 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7039 {\r
7040   char buf[MSG_SIZ];\r
7041   char *p;\r
7042   static BOOL sendNextChar = FALSE;\r
7043   static BOOL quoteNextChar = FALSE;\r
7044   InputSource *is = consoleInputSource;\r
7045   CHARFORMAT cf;\r
7046   CHARRANGE sel;\r
7047 \r
7048   switch (message) {\r
7049   case WM_CHAR:\r
7050     if (!appData.localLineEditing || sendNextChar) {\r
7051       is->buf[0] = (CHAR) wParam;\r
7052       is->count = 1;\r
7053       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7054       sendNextChar = FALSE;\r
7055       return 0;\r
7056     }\r
7057     if (quoteNextChar) {\r
7058       buf[0] = (char) wParam;\r
7059       buf[1] = NULLCHAR;\r
7060       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7061       quoteNextChar = FALSE;\r
7062       return 0;\r
7063     }\r
7064     switch (wParam) {\r
7065     case '\r':   /* Enter key */\r
7066       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7067       if (consoleEcho) SaveInHistory(is->buf);\r
7068       is->buf[is->count++] = '\n';\r
7069       is->buf[is->count] = NULLCHAR;\r
7070       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7071       if (consoleEcho) {\r
7072         ConsoleOutput(is->buf, is->count, TRUE);\r
7073       } else if (appData.localLineEditing) {\r
7074         ConsoleOutput("\n", 1, TRUE);\r
7075       }\r
7076       /* fall thru */\r
7077     case '\033': /* Escape key */\r
7078       SetWindowText(hwnd, "");\r
7079       cf.cbSize = sizeof(CHARFORMAT);\r
7080       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7081       if (consoleEcho) {\r
7082         cf.crTextColor = textAttribs[ColorNormal].color;\r
7083       } else {\r
7084         cf.crTextColor = COLOR_ECHOOFF;\r
7085       }\r
7086       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7087       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7088       return 0;\r
7089     case '\t':   /* Tab key */\r
7090       if (GetKeyState(VK_SHIFT) < 0) {\r
7091         /* shifted */\r
7092         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7093       } else {\r
7094         /* unshifted */\r
7095         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7096         if (buttonDesc[0].hwnd) {\r
7097           SetFocus(buttonDesc[0].hwnd);\r
7098         } else {\r
7099           SetFocus(hwndMain);\r
7100         }\r
7101       }\r
7102       return 0;\r
7103     case '\023': /* Ctrl+S */\r
7104       sendNextChar = TRUE;\r
7105       return 0;\r
7106     case '\021': /* Ctrl+Q */\r
7107       quoteNextChar = TRUE;\r
7108       return 0;\r
7109     JAWS_REPLAY\r
7110     default:\r
7111       break;\r
7112     }\r
7113     break;\r
7114   case WM_KEYDOWN:\r
7115     switch (wParam) {\r
7116     case VK_UP:\r
7117       GetWindowText(hwnd, buf, MSG_SIZ);\r
7118       p = PrevInHistory(buf);\r
7119       if (p != NULL) {\r
7120         SetWindowText(hwnd, p);\r
7121         sel.cpMin = 999999;\r
7122         sel.cpMax = 999999;\r
7123         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7124         return 0;\r
7125       }\r
7126       break;\r
7127     case VK_DOWN:\r
7128       p = NextInHistory();\r
7129       if (p != NULL) {\r
7130         SetWindowText(hwnd, p);\r
7131         sel.cpMin = 999999;\r
7132         sel.cpMax = 999999;\r
7133         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7134         return 0;\r
7135       }\r
7136       break;\r
7137     case VK_HOME:\r
7138     case VK_END:\r
7139       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7140       /* fall thru */\r
7141     case VK_PRIOR:\r
7142     case VK_NEXT:\r
7143       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7144       return 0;\r
7145     }\r
7146     break;\r
7147   case WM_MBUTTONDOWN:\r
7148     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7149       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7150     break;\r
7151   case WM_RBUTTONUP:\r
7152     if (GetKeyState(VK_SHIFT) & ~1) {\r
7153       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7154         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7155     } else {\r
7156       POINT pt;\r
7157       HMENU hmenu;\r
7158       hmenu = LoadMenu(hInst, "InputMenu");\r
7159       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7160       if (sel.cpMin == sel.cpMax) {\r
7161         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7162         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7163       }\r
7164       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7165         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7166       }\r
7167       pt.x = LOWORD(lParam);\r
7168       pt.y = HIWORD(lParam);\r
7169       MenuPopup(hwnd, pt, hmenu, -1);\r
7170     }\r
7171     return 0;\r
7172   case WM_COMMAND:\r
7173     switch (LOWORD(wParam)) { \r
7174     case IDM_Undo:\r
7175       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7176       return 0;\r
7177     case IDM_SelectAll:\r
7178       sel.cpMin = 0;\r
7179       sel.cpMax = -1; /*999999?*/\r
7180       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7181       return 0;\r
7182     case IDM_Cut:\r
7183       SendMessage(hwnd, WM_CUT, 0, 0);\r
7184       return 0;\r
7185     case IDM_Paste:\r
7186       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7187       return 0;\r
7188     case IDM_Copy:\r
7189       SendMessage(hwnd, WM_COPY, 0, 0);\r
7190       return 0;\r
7191     }\r
7192     break;\r
7193   }\r
7194   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7195 }\r
7196 \r
7197 #define CO_MAX  100000\r
7198 #define CO_TRIM   1000\r
7199 \r
7200 LRESULT CALLBACK\r
7201 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7202 {\r
7203   static SnapData sd;\r
7204   HWND hText, hInput;\r
7205   RECT rect;\r
7206   static int sizeX, sizeY;\r
7207   int newSizeX, newSizeY;\r
7208   MINMAXINFO *mmi;\r
7209   WORD wMask;\r
7210 \r
7211   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7212   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7213 \r
7214   switch (message) {\r
7215   case WM_NOTIFY:\r
7216     if (((NMHDR*)lParam)->code == EN_LINK)\r
7217     {\r
7218       ENLINK *pLink = (ENLINK*)lParam;\r
7219       if (pLink->msg == WM_LBUTTONUP)\r
7220       {\r
7221         TEXTRANGE tr;\r
7222 \r
7223         tr.chrg = pLink->chrg;\r
7224         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7225         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7226         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7227         free(tr.lpstrText);\r
7228       }\r
7229     }\r
7230     break;\r
7231   case WM_INITDIALOG: /* message: initialize dialog box */\r
7232     hwndConsole = hDlg;\r
7233     SetFocus(hInput);\r
7234     consoleTextWindowProc = (WNDPROC)\r
7235       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7236     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7237     consoleInputWindowProc = (WNDPROC)\r
7238       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7239     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7240     Colorize(ColorNormal, TRUE);\r
7241     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7242     ChangedConsoleFont();\r
7243     GetClientRect(hDlg, &rect);\r
7244     sizeX = rect.right;\r
7245     sizeY = rect.bottom;\r
7246     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7247         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7248       WINDOWPLACEMENT wp;\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    // [HGM] Chessknight's change 2004-07-13\r
7262    else { /* Determine Defaults */\r
7263        WINDOWPLACEMENT wp;\r
7264        wpConsole.x = wpMain.width + 1;\r
7265        wpConsole.y = wpMain.y;\r
7266        wpConsole.width = screenWidth -  wpMain.width;\r
7267        wpConsole.height = wpMain.height;\r
7268        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7269        wp.length = sizeof(WINDOWPLACEMENT);\r
7270        wp.flags = 0;\r
7271        wp.showCmd = SW_SHOW;\r
7272        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7273        wp.rcNormalPosition.left = wpConsole.x;\r
7274        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7275        wp.rcNormalPosition.top = wpConsole.y;\r
7276        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7277        SetWindowPlacement(hDlg, &wp);\r
7278     }\r
7279 \r
7280    // Allow hText to highlight URLs and send notifications on them\r
7281    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7282    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7283    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7284    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7285 \r
7286     return FALSE;\r
7287 \r
7288   case WM_SETFOCUS:\r
7289     SetFocus(hInput);\r
7290     return 0;\r
7291 \r
7292   case WM_CLOSE:\r
7293     ExitEvent(0);\r
7294     /* not reached */\r
7295     break;\r
7296 \r
7297   case WM_SIZE:\r
7298     if (IsIconic(hDlg)) break;\r
7299     newSizeX = LOWORD(lParam);\r
7300     newSizeY = HIWORD(lParam);\r
7301     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7302       RECT rectText, rectInput;\r
7303       POINT pt;\r
7304       int newTextHeight, newTextWidth;\r
7305       GetWindowRect(hText, &rectText);\r
7306       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7307       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7308       if (newTextHeight < 0) {\r
7309         newSizeY += -newTextHeight;\r
7310         newTextHeight = 0;\r
7311       }\r
7312       SetWindowPos(hText, NULL, 0, 0,\r
7313         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7314       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7315       pt.x = rectInput.left;\r
7316       pt.y = rectInput.top + newSizeY - sizeY;\r
7317       ScreenToClient(hDlg, &pt);\r
7318       SetWindowPos(hInput, NULL, \r
7319         pt.x, pt.y, /* needs client coords */   \r
7320         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7321         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7322     }\r
7323     sizeX = newSizeX;\r
7324     sizeY = newSizeY;\r
7325     break;\r
7326 \r
7327   case WM_GETMINMAXINFO:\r
7328     /* Prevent resizing window too small */\r
7329     mmi = (MINMAXINFO *) lParam;\r
7330     mmi->ptMinTrackSize.x = 100;\r
7331     mmi->ptMinTrackSize.y = 100;\r
7332     break;\r
7333 \r
7334   /* [AS] Snapping */\r
7335   case WM_ENTERSIZEMOVE:\r
7336     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7337 \r
7338   case WM_SIZING:\r
7339     return OnSizing( &sd, hDlg, wParam, lParam );\r
7340 \r
7341   case WM_MOVING:\r
7342     return OnMoving( &sd, hDlg, wParam, lParam );\r
7343 \r
7344   case WM_EXITSIZEMOVE:\r
7345         UpdateICSWidth(hText);\r
7346     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7347   }\r
7348 \r
7349   return DefWindowProc(hDlg, message, wParam, lParam);\r
7350 }\r
7351 \r
7352 \r
7353 VOID\r
7354 ConsoleCreate()\r
7355 {\r
7356   HWND hCons;\r
7357   if (hwndConsole) return;\r
7358   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7359   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7360 }\r
7361 \r
7362 \r
7363 VOID\r
7364 ConsoleOutput(char* data, int length, int forceVisible)\r
7365 {\r
7366   HWND hText;\r
7367   int trim, exlen;\r
7368   char *p, *q;\r
7369   char buf[CO_MAX+1];\r
7370   POINT pEnd;\r
7371   RECT rect;\r
7372   static int delayLF = 0;\r
7373   CHARRANGE savesel, sel;\r
7374 \r
7375   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7376   p = data;\r
7377   q = buf;\r
7378   if (delayLF) {\r
7379     *q++ = '\r';\r
7380     *q++ = '\n';\r
7381     delayLF = 0;\r
7382   }\r
7383   while (length--) {\r
7384     if (*p == '\n') {\r
7385       if (*++p) {\r
7386         *q++ = '\r';\r
7387         *q++ = '\n';\r
7388       } else {\r
7389         delayLF = 1;\r
7390       }\r
7391     } else if (*p == '\007') {\r
7392        MyPlaySound(&sounds[(int)SoundBell]);\r
7393        p++;\r
7394     } else {\r
7395       *q++ = *p++;\r
7396     }\r
7397   }\r
7398   *q = NULLCHAR;\r
7399   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7400   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7401   /* Save current selection */\r
7402   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7403   exlen = GetWindowTextLength(hText);\r
7404   /* Find out whether current end of text is visible */\r
7405   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7406   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7407   /* Trim existing text if it's too long */\r
7408   if (exlen + (q - buf) > CO_MAX) {\r
7409     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7410     sel.cpMin = 0;\r
7411     sel.cpMax = trim;\r
7412     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7413     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7414     exlen -= trim;\r
7415     savesel.cpMin -= trim;\r
7416     savesel.cpMax -= trim;\r
7417     if (exlen < 0) exlen = 0;\r
7418     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7419     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7420   }\r
7421   /* Append the new text */\r
7422   sel.cpMin = exlen;\r
7423   sel.cpMax = exlen;\r
7424   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7425   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7426   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7427   if (forceVisible || exlen == 0 ||\r
7428       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7429        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7430     /* Scroll to make new end of text visible if old end of text\r
7431        was visible or new text is an echo of user typein */\r
7432     sel.cpMin = 9999999;\r
7433     sel.cpMax = 9999999;\r
7434     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7435     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7436     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7437     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7438   }\r
7439   if (savesel.cpMax == exlen || forceVisible) {\r
7440     /* Move insert point to new end of text if it was at the old\r
7441        end of text or if the new text is an echo of user typein */\r
7442     sel.cpMin = 9999999;\r
7443     sel.cpMax = 9999999;\r
7444     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7445   } else {\r
7446     /* Restore previous selection */\r
7447     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7448   }\r
7449   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7450 }\r
7451 \r
7452 /*---------*/\r
7453 \r
7454 \r
7455 void\r
7456 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7457 {\r
7458   char buf[100];\r
7459   char *str;\r
7460   COLORREF oldFg, oldBg;\r
7461   HFONT oldFont;\r
7462   RECT rect;\r
7463 \r
7464   if(copyNumber > 1)\r
7465     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7466 \r
7467   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7468   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7469   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7470 \r
7471   rect.left = x;\r
7472   rect.right = x + squareSize;\r
7473   rect.top  = y;\r
7474   rect.bottom = y + squareSize;\r
7475   str = buf;\r
7476 \r
7477   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7478                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7479              y, ETO_CLIPPED|ETO_OPAQUE,\r
7480              &rect, str, strlen(str), NULL);\r
7481 \r
7482   (void) SetTextColor(hdc, oldFg);\r
7483   (void) SetBkColor(hdc, oldBg);\r
7484   (void) SelectObject(hdc, oldFont);\r
7485 }\r
7486 \r
7487 void\r
7488 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7489               RECT *rect, char *color, char *flagFell)\r
7490 {\r
7491   char buf[100];\r
7492   char *str;\r
7493   COLORREF oldFg, oldBg;\r
7494   HFONT oldFont;\r
7495 \r
7496   if (appData.clockMode) {\r
7497     if (tinyLayout)\r
7498       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7499     else\r
7500       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7501     str = buf;\r
7502   } else {\r
7503     str = color;\r
7504   }\r
7505 \r
7506   if (highlight) {\r
7507     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7508     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7509   } else {\r
7510     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7511     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7512   }\r
7513   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7514 \r
7515   JAWS_SILENCE\r
7516 \r
7517   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7518              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7519              rect, str, strlen(str), NULL);\r
7520   if(logoHeight > 0 && appData.clockMode) {\r
7521       RECT r;\r
7522       str += strlen(color)+2;\r
7523       r.top = rect->top + logoHeight/2;\r
7524       r.left = rect->left;\r
7525       r.right = rect->right;\r
7526       r.bottom = rect->bottom;\r
7527       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7528                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7529                  &r, str, strlen(str), NULL);\r
7530   }\r
7531   (void) SetTextColor(hdc, oldFg);\r
7532   (void) SetBkColor(hdc, oldBg);\r
7533   (void) SelectObject(hdc, oldFont);\r
7534 }\r
7535 \r
7536 \r
7537 int\r
7538 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7539            OVERLAPPED *ovl)\r
7540 {\r
7541   int ok, err;\r
7542 \r
7543   /* [AS]  */\r
7544   if( count <= 0 ) {\r
7545     if (appData.debugMode) {\r
7546       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7547     }\r
7548 \r
7549     return ERROR_INVALID_USER_BUFFER;\r
7550   }\r
7551 \r
7552   ResetEvent(ovl->hEvent);\r
7553   ovl->Offset = ovl->OffsetHigh = 0;\r
7554   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7555   if (ok) {\r
7556     err = NO_ERROR;\r
7557   } else {\r
7558     err = GetLastError();\r
7559     if (err == ERROR_IO_PENDING) {\r
7560       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7561       if (ok)\r
7562         err = NO_ERROR;\r
7563       else\r
7564         err = GetLastError();\r
7565     }\r
7566   }\r
7567   return err;\r
7568 }\r
7569 \r
7570 int\r
7571 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7572             OVERLAPPED *ovl)\r
7573 {\r
7574   int ok, err;\r
7575 \r
7576   ResetEvent(ovl->hEvent);\r
7577   ovl->Offset = ovl->OffsetHigh = 0;\r
7578   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7579   if (ok) {\r
7580     err = NO_ERROR;\r
7581   } else {\r
7582     err = GetLastError();\r
7583     if (err == ERROR_IO_PENDING) {\r
7584       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7585       if (ok)\r
7586         err = NO_ERROR;\r
7587       else\r
7588         err = GetLastError();\r
7589     }\r
7590   }\r
7591   return err;\r
7592 }\r
7593 \r
7594 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7595 void CheckForInputBufferFull( InputSource * is )\r
7596 {\r
7597     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7598         /* Look for end of line */\r
7599         char * p = is->buf;\r
7600         \r
7601         while( p < is->next && *p != '\n' ) {\r
7602             p++;\r
7603         }\r
7604 \r
7605         if( p >= is->next ) {\r
7606             if (appData.debugMode) {\r
7607                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7608             }\r
7609 \r
7610             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7611             is->count = (DWORD) -1;\r
7612             is->next = is->buf;\r
7613         }\r
7614     }\r
7615 }\r
7616 \r
7617 DWORD\r
7618 InputThread(LPVOID arg)\r
7619 {\r
7620   InputSource *is;\r
7621   OVERLAPPED ovl;\r
7622 \r
7623   is = (InputSource *) arg;\r
7624   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7625   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7626   while (is->hThread != NULL) {\r
7627     is->error = DoReadFile(is->hFile, is->next,\r
7628                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7629                            &is->count, &ovl);\r
7630     if (is->error == NO_ERROR) {\r
7631       is->next += is->count;\r
7632     } else {\r
7633       if (is->error == ERROR_BROKEN_PIPE) {\r
7634         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7635         is->count = 0;\r
7636       } else {\r
7637         is->count = (DWORD) -1;\r
7638         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7639         break; \r
7640       }\r
7641     }\r
7642 \r
7643     CheckForInputBufferFull( is );\r
7644 \r
7645     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7646 \r
7647     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7648 \r
7649     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7650   }\r
7651 \r
7652   CloseHandle(ovl.hEvent);\r
7653   CloseHandle(is->hFile);\r
7654 \r
7655   if (appData.debugMode) {\r
7656     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7657   }\r
7658 \r
7659   return 0;\r
7660 }\r
7661 \r
7662 \r
7663 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7664 DWORD\r
7665 NonOvlInputThread(LPVOID arg)\r
7666 {\r
7667   InputSource *is;\r
7668   char *p, *q;\r
7669   int i;\r
7670   char prev;\r
7671 \r
7672   is = (InputSource *) arg;\r
7673   while (is->hThread != NULL) {\r
7674     is->error = ReadFile(is->hFile, is->next,\r
7675                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7676                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7677     if (is->error == NO_ERROR) {\r
7678       /* Change CRLF to LF */\r
7679       if (is->next > is->buf) {\r
7680         p = is->next - 1;\r
7681         i = is->count + 1;\r
7682       } else {\r
7683         p = is->next;\r
7684         i = is->count;\r
7685       }\r
7686       q = p;\r
7687       prev = NULLCHAR;\r
7688       while (i > 0) {\r
7689         if (prev == '\r' && *p == '\n') {\r
7690           *(q-1) = '\n';\r
7691           is->count--;\r
7692         } else { \r
7693           *q++ = *p;\r
7694         }\r
7695         prev = *p++;\r
7696         i--;\r
7697       }\r
7698       *q = NULLCHAR;\r
7699       is->next = q;\r
7700     } else {\r
7701       if (is->error == ERROR_BROKEN_PIPE) {\r
7702         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7703         is->count = 0; \r
7704       } else {\r
7705         is->count = (DWORD) -1;\r
7706       }\r
7707     }\r
7708 \r
7709     CheckForInputBufferFull( is );\r
7710 \r
7711     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7712 \r
7713     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7714 \r
7715     if (is->count < 0) break;  /* Quit on error */\r
7716   }\r
7717   CloseHandle(is->hFile);\r
7718   return 0;\r
7719 }\r
7720 \r
7721 DWORD\r
7722 SocketInputThread(LPVOID arg)\r
7723 {\r
7724   InputSource *is;\r
7725 \r
7726   is = (InputSource *) arg;\r
7727   while (is->hThread != NULL) {\r
7728     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7729     if ((int)is->count == SOCKET_ERROR) {\r
7730       is->count = (DWORD) -1;\r
7731       is->error = WSAGetLastError();\r
7732     } else {\r
7733       is->error = NO_ERROR;\r
7734       is->next += is->count;\r
7735       if (is->count == 0 && is->second == is) {\r
7736         /* End of file on stderr; quit with no message */\r
7737         break;\r
7738       }\r
7739     }\r
7740     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7741 \r
7742     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7743 \r
7744     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7745   }\r
7746   return 0;\r
7747 }\r
7748 \r
7749 VOID\r
7750 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7751 {\r
7752   InputSource *is;\r
7753 \r
7754   is = (InputSource *) lParam;\r
7755   if (is->lineByLine) {\r
7756     /* Feed in lines one by one */\r
7757     char *p = is->buf;\r
7758     char *q = p;\r
7759     while (q < is->next) {\r
7760       if (*q++ == '\n') {\r
7761         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7762         p = q;\r
7763       }\r
7764     }\r
7765     \r
7766     /* Move any partial line to the start of the buffer */\r
7767     q = is->buf;\r
7768     while (p < is->next) {\r
7769       *q++ = *p++;\r
7770     }\r
7771     is->next = q;\r
7772 \r
7773     if (is->error != NO_ERROR || is->count == 0) {\r
7774       /* Notify backend of the error.  Note: If there was a partial\r
7775          line at the end, it is not flushed through. */\r
7776       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7777     }\r
7778   } else {\r
7779     /* Feed in the whole chunk of input at once */\r
7780     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7781     is->next = is->buf;\r
7782   }\r
7783 }\r
7784 \r
7785 /*---------------------------------------------------------------------------*\\r
7786  *\r
7787  *  Menu enables. Used when setting various modes.\r
7788  *\r
7789 \*---------------------------------------------------------------------------*/\r
7790 \r
7791 typedef struct {\r
7792   int item;\r
7793   int flags;\r
7794 } Enables;\r
7795 \r
7796 VOID\r
7797 GreyRevert(Boolean grey)\r
7798 { // [HGM] vari: for retracting variations in local mode\r
7799   HMENU hmenu = GetMenu(hwndMain);\r
7800   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7801   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7802 }\r
7803 \r
7804 VOID\r
7805 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7806 {\r
7807   while (enab->item > 0) {\r
7808     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7809     enab++;\r
7810   }\r
7811 }\r
7812 \r
7813 Enables gnuEnables[] = {\r
7814   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7827 \r
7828   // Needed to switch from ncp to GNU mode on Engine Load\r
7829   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7830   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7833   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7835   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7836   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7837   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7843   { -1, -1 }\r
7844 };\r
7845 \r
7846 Enables icsEnables[] = {\r
7847   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7866   { -1, -1 }\r
7867 };\r
7868 \r
7869 #if ZIPPY\r
7870 Enables zippyEnables[] = {\r
7871   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7875   { -1, -1 }\r
7876 };\r
7877 #endif\r
7878 \r
7879 Enables ncpEnables[] = {\r
7880   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7889   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7890   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7902   { -1, -1 }\r
7903 };\r
7904 \r
7905 Enables trainingOnEnables[] = {\r
7906   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7915   { -1, -1 }\r
7916 };\r
7917 \r
7918 Enables trainingOffEnables[] = {\r
7919   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7928   { -1, -1 }\r
7929 };\r
7930 \r
7931 /* These modify either ncpEnables or gnuEnables */\r
7932 Enables cmailEnables[] = {\r
7933   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7935   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7936   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7940   { -1, -1 }\r
7941 };\r
7942 \r
7943 Enables machineThinkingEnables[] = {\r
7944   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7957 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7960   { -1, -1 }\r
7961 };\r
7962 \r
7963 Enables userThinkingEnables[] = {\r
7964   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7965   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7967   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7968   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7969   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7971   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7977 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7980   { -1, -1 }\r
7981 };\r
7982 \r
7983 /*---------------------------------------------------------------------------*\\r
7984  *\r
7985  *  Front-end interface functions exported by XBoard.\r
7986  *  Functions appear in same order as prototypes in frontend.h.\r
7987  * \r
7988 \*---------------------------------------------------------------------------*/\r
7989 VOID\r
7990 CheckMark(UINT item, int state)\r
7991 {\r
7992     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7993 }\r
7994 \r
7995 VOID\r
7996 ModeHighlight()\r
7997 {\r
7998   static UINT prevChecked = 0;\r
7999   static int prevPausing = 0;\r
8000   UINT nowChecked;\r
8001 \r
8002   if (pausing != prevPausing) {\r
8003     prevPausing = pausing;\r
8004     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8005                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8006     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8007   }\r
8008 \r
8009   switch (gameMode) {\r
8010   case BeginningOfGame:\r
8011     if (appData.icsActive)\r
8012       nowChecked = IDM_IcsClient;\r
8013     else if (appData.noChessProgram)\r
8014       nowChecked = IDM_EditGame;\r
8015     else\r
8016       nowChecked = IDM_MachineBlack;\r
8017     break;\r
8018   case MachinePlaysBlack:\r
8019     nowChecked = IDM_MachineBlack;\r
8020     break;\r
8021   case MachinePlaysWhite:\r
8022     nowChecked = IDM_MachineWhite;\r
8023     break;\r
8024   case TwoMachinesPlay:\r
8025     nowChecked = IDM_TwoMachines;\r
8026     break;\r
8027   case AnalyzeMode:\r
8028     nowChecked = IDM_AnalysisMode;\r
8029     break;\r
8030   case AnalyzeFile:\r
8031     nowChecked = IDM_AnalyzeFile;\r
8032     break;\r
8033   case EditGame:\r
8034     nowChecked = IDM_EditGame;\r
8035     break;\r
8036   case PlayFromGameFile:\r
8037     nowChecked = IDM_LoadGame;\r
8038     break;\r
8039   case EditPosition:\r
8040     nowChecked = IDM_EditPosition;\r
8041     break;\r
8042   case Training:\r
8043     nowChecked = IDM_Training;\r
8044     break;\r
8045   case IcsPlayingWhite:\r
8046   case IcsPlayingBlack:\r
8047   case IcsObserving:\r
8048   case IcsIdle:\r
8049     nowChecked = IDM_IcsClient;\r
8050     break;\r
8051   default:\r
8052   case EndOfGame:\r
8053     nowChecked = 0;\r
8054     break;\r
8055   }\r
8056   CheckMark(prevChecked, MF_UNCHECKED);\r
8057   CheckMark(nowChecked, MF_CHECKED);\r
8058   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8059 \r
8060   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8061     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8062                           MF_BYCOMMAND|MF_ENABLED);\r
8063   } else {\r
8064     (void) EnableMenuItem(GetMenu(hwndMain), \r
8065                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8066   }\r
8067 \r
8068   prevChecked = nowChecked;\r
8069 \r
8070   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8071   if (appData.icsActive) {\r
8072        if (appData.icsEngineAnalyze) {\r
8073                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8074        } else {\r
8075                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8076        }\r
8077   }\r
8078   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8079 }\r
8080 \r
8081 VOID\r
8082 SetICSMode()\r
8083 {\r
8084   HMENU hmenu = GetMenu(hwndMain);\r
8085   SetMenuEnables(hmenu, icsEnables);\r
8086   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8087     MF_BYCOMMAND|MF_ENABLED);\r
8088 #if ZIPPY\r
8089   if (appData.zippyPlay) {\r
8090     SetMenuEnables(hmenu, zippyEnables);\r
8091     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8092          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8093           MF_BYCOMMAND|MF_ENABLED);\r
8094   }\r
8095 #endif\r
8096 }\r
8097 \r
8098 VOID\r
8099 SetGNUMode()\r
8100 {\r
8101   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8102 }\r
8103 \r
8104 VOID\r
8105 SetNCPMode()\r
8106 {\r
8107   HMENU hmenu = GetMenu(hwndMain);\r
8108   SetMenuEnables(hmenu, ncpEnables);\r
8109     DrawMenuBar(hwndMain);\r
8110 }\r
8111 \r
8112 VOID\r
8113 SetCmailMode()\r
8114 {\r
8115   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8116 }\r
8117 \r
8118 VOID \r
8119 SetTrainingModeOn()\r
8120 {\r
8121   int i;\r
8122   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8123   for (i = 0; i < N_BUTTONS; i++) {\r
8124     if (buttonDesc[i].hwnd != NULL)\r
8125       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8126   }\r
8127   CommentPopDown();\r
8128 }\r
8129 \r
8130 VOID SetTrainingModeOff()\r
8131 {\r
8132   int i;\r
8133   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8134   for (i = 0; i < N_BUTTONS; i++) {\r
8135     if (buttonDesc[i].hwnd != NULL)\r
8136       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8137   }\r
8138 }\r
8139 \r
8140 \r
8141 VOID\r
8142 SetUserThinkingEnables()\r
8143 {\r
8144   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8145 }\r
8146 \r
8147 VOID\r
8148 SetMachineThinkingEnables()\r
8149 {\r
8150   HMENU hMenu = GetMenu(hwndMain);\r
8151   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8152 \r
8153   SetMenuEnables(hMenu, machineThinkingEnables);\r
8154 \r
8155   if (gameMode == MachinePlaysBlack) {\r
8156     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8157   } else if (gameMode == MachinePlaysWhite) {\r
8158     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8159   } else if (gameMode == TwoMachinesPlay) {\r
8160     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8161   }\r
8162 }\r
8163 \r
8164 \r
8165 VOID\r
8166 DisplayTitle(char *str)\r
8167 {\r
8168   char title[MSG_SIZ], *host;\r
8169   if (str[0] != NULLCHAR) {\r
8170     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8171   } else if (appData.icsActive) {\r
8172     if (appData.icsCommPort[0] != NULLCHAR)\r
8173       host = "ICS";\r
8174     else \r
8175       host = appData.icsHost;\r
8176       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8177   } else if (appData.noChessProgram) {\r
8178     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8179   } else {\r
8180     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8181     strcat(title, ": ");\r
8182     strcat(title, first.tidy);\r
8183   }\r
8184   SetWindowText(hwndMain, title);\r
8185 }\r
8186 \r
8187 \r
8188 VOID\r
8189 DisplayMessage(char *str1, char *str2)\r
8190 {\r
8191   HDC hdc;\r
8192   HFONT oldFont;\r
8193   int remain = MESSAGE_TEXT_MAX - 1;\r
8194   int len;\r
8195 \r
8196   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8197   messageText[0] = NULLCHAR;\r
8198   if (*str1) {\r
8199     len = strlen(str1);\r
8200     if (len > remain) len = remain;\r
8201     strncpy(messageText, str1, len);\r
8202     messageText[len] = NULLCHAR;\r
8203     remain -= len;\r
8204   }\r
8205   if (*str2 && remain >= 2) {\r
8206     if (*str1) {\r
8207       strcat(messageText, "  ");\r
8208       remain -= 2;\r
8209     }\r
8210     len = strlen(str2);\r
8211     if (len > remain) len = remain;\r
8212     strncat(messageText, str2, len);\r
8213   }\r
8214   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8215   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8216 \r
8217   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8218 \r
8219   SAYMACHINEMOVE();\r
8220 \r
8221   hdc = GetDC(hwndMain);\r
8222   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8223   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8224              &messageRect, messageText, strlen(messageText), NULL);\r
8225   (void) SelectObject(hdc, oldFont);\r
8226   (void) ReleaseDC(hwndMain, hdc);\r
8227 }\r
8228 \r
8229 VOID\r
8230 DisplayError(char *str, int error)\r
8231 {\r
8232   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8233   int len;\r
8234 \r
8235   if (error == 0) {\r
8236     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8237   } else {\r
8238     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8239                         NULL, error, LANG_NEUTRAL,\r
8240                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8241     if (len > 0) {\r
8242       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8243     } else {\r
8244       ErrorMap *em = errmap;\r
8245       while (em->err != 0 && em->err != error) em++;\r
8246       if (em->err != 0) {\r
8247         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8248       } else {\r
8249         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8250       }\r
8251     }\r
8252   }\r
8253   \r
8254   ErrorPopUp(_("Error"), buf);\r
8255 }\r
8256 \r
8257 \r
8258 VOID\r
8259 DisplayMoveError(char *str)\r
8260 {\r
8261   fromX = fromY = -1;\r
8262   ClearHighlights();\r
8263   DrawPosition(FALSE, NULL);\r
8264   if (appData.popupMoveErrors) {\r
8265     ErrorPopUp(_("Error"), str);\r
8266   } else {\r
8267     DisplayMessage(str, "");\r
8268     moveErrorMessageUp = TRUE;\r
8269   }\r
8270 }\r
8271 \r
8272 VOID\r
8273 DisplayFatalError(char *str, int error, int exitStatus)\r
8274 {\r
8275   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8276   int len;\r
8277   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8278 \r
8279   if (error != 0) {\r
8280     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8281                         NULL, error, LANG_NEUTRAL,\r
8282                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8283     if (len > 0) {\r
8284       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8285     } else {\r
8286       ErrorMap *em = errmap;\r
8287       while (em->err != 0 && em->err != error) em++;\r
8288       if (em->err != 0) {\r
8289         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8290       } else {\r
8291         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8292       }\r
8293     }\r
8294     str = buf;\r
8295   }\r
8296   if (appData.debugMode) {\r
8297     fprintf(debugFP, "%s: %s\n", label, str);\r
8298   }\r
8299   if (appData.popupExitMessage) {\r
8300     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8301                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8302   }\r
8303   ExitEvent(exitStatus);\r
8304 }\r
8305 \r
8306 \r
8307 VOID\r
8308 DisplayInformation(char *str)\r
8309 {\r
8310   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8311 }\r
8312 \r
8313 \r
8314 VOID\r
8315 DisplayNote(char *str)\r
8316 {\r
8317   ErrorPopUp(_("Note"), str);\r
8318 }\r
8319 \r
8320 \r
8321 typedef struct {\r
8322   char *title, *question, *replyPrefix;\r
8323   ProcRef pr;\r
8324 } QuestionParams;\r
8325 \r
8326 LRESULT CALLBACK\r
8327 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8328 {\r
8329   static QuestionParams *qp;\r
8330   char reply[MSG_SIZ];\r
8331   int len, err;\r
8332 \r
8333   switch (message) {\r
8334   case WM_INITDIALOG:\r
8335     qp = (QuestionParams *) lParam;\r
8336     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8337     Translate(hDlg, DLG_Question);\r
8338     SetWindowText(hDlg, qp->title);\r
8339     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8340     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8341     return FALSE;\r
8342 \r
8343   case WM_COMMAND:\r
8344     switch (LOWORD(wParam)) {\r
8345     case IDOK:\r
8346       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8347       if (*reply) strcat(reply, " ");\r
8348       len = strlen(reply);\r
8349       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8350       strcat(reply, "\n");\r
8351       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8352       EndDialog(hDlg, TRUE);\r
8353       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8354       return TRUE;\r
8355     case IDCANCEL:\r
8356       EndDialog(hDlg, FALSE);\r
8357       return TRUE;\r
8358     default:\r
8359       break;\r
8360     }\r
8361     break;\r
8362   }\r
8363   return FALSE;\r
8364 }\r
8365 \r
8366 VOID\r
8367 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8368 {\r
8369     QuestionParams qp;\r
8370     FARPROC lpProc;\r
8371     \r
8372     qp.title = title;\r
8373     qp.question = question;\r
8374     qp.replyPrefix = replyPrefix;\r
8375     qp.pr = pr;\r
8376     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8377     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8378       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8379     FreeProcInstance(lpProc);\r
8380 }\r
8381 \r
8382 /* [AS] Pick FRC position */\r
8383 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8384 {\r
8385     static int * lpIndexFRC;\r
8386     BOOL index_is_ok;\r
8387     char buf[16];\r
8388 \r
8389     switch( message )\r
8390     {\r
8391     case WM_INITDIALOG:\r
8392         lpIndexFRC = (int *) lParam;\r
8393 \r
8394         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8395         Translate(hDlg, DLG_NewGameFRC);\r
8396 \r
8397         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8398         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8399         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8400         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8401 \r
8402         break;\r
8403 \r
8404     case WM_COMMAND:\r
8405         switch( LOWORD(wParam) ) {\r
8406         case IDOK:\r
8407             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8408             EndDialog( hDlg, 0 );\r
8409             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8410             return TRUE;\r
8411         case IDCANCEL:\r
8412             EndDialog( hDlg, 1 );   \r
8413             return TRUE;\r
8414         case IDC_NFG_Edit:\r
8415             if( HIWORD(wParam) == EN_CHANGE ) {\r
8416                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8417 \r
8418                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8419             }\r
8420             return TRUE;\r
8421         case IDC_NFG_Random:\r
8422           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8423             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8424             return TRUE;\r
8425         }\r
8426 \r
8427         break;\r
8428     }\r
8429 \r
8430     return FALSE;\r
8431 }\r
8432 \r
8433 int NewGameFRC()\r
8434 {\r
8435     int result;\r
8436     int index = appData.defaultFrcPosition;\r
8437     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8438 \r
8439     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8440 \r
8441     if( result == 0 ) {\r
8442         appData.defaultFrcPosition = index;\r
8443     }\r
8444 \r
8445     return result;\r
8446 }\r
8447 \r
8448 /* [AS] Game list options. Refactored by HGM */\r
8449 \r
8450 HWND gameListOptionsDialog;\r
8451 \r
8452 // low-level front-end: clear text edit / list widget\r
8453 void\r
8454 GLT_ClearList()\r
8455 {\r
8456     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8457 }\r
8458 \r
8459 // low-level front-end: clear text edit / list widget\r
8460 void\r
8461 GLT_DeSelectList()\r
8462 {\r
8463     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8464 }\r
8465 \r
8466 // low-level front-end: append line to text edit / list widget\r
8467 void\r
8468 GLT_AddToList( char *name )\r
8469 {\r
8470     if( name != 0 ) {\r
8471             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8472     }\r
8473 }\r
8474 \r
8475 // low-level front-end: get line from text edit / list widget\r
8476 Boolean\r
8477 GLT_GetFromList( int index, char *name )\r
8478 {\r
8479     if( name != 0 ) {\r
8480             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8481                 return TRUE;\r
8482     }\r
8483     return FALSE;\r
8484 }\r
8485 \r
8486 void GLT_MoveSelection( HWND hDlg, int delta )\r
8487 {\r
8488     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8489     int idx2 = idx1 + delta;\r
8490     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8491 \r
8492     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8493         char buf[128];\r
8494 \r
8495         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8496         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8497         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8498         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8499     }\r
8500 }\r
8501 \r
8502 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8503 {\r
8504     switch( message )\r
8505     {\r
8506     case WM_INITDIALOG:\r
8507         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8508         \r
8509         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8510         Translate(hDlg, DLG_GameListOptions);\r
8511 \r
8512         /* Initialize list */\r
8513         GLT_TagsToList( lpUserGLT );\r
8514 \r
8515         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8516 \r
8517         break;\r
8518 \r
8519     case WM_COMMAND:\r
8520         switch( LOWORD(wParam) ) {\r
8521         case IDOK:\r
8522             GLT_ParseList();\r
8523             EndDialog( hDlg, 0 );\r
8524             return TRUE;\r
8525         case IDCANCEL:\r
8526             EndDialog( hDlg, 1 );\r
8527             return TRUE;\r
8528 \r
8529         case IDC_GLT_Default:\r
8530             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8531             return TRUE;\r
8532 \r
8533         case IDC_GLT_Restore:\r
8534             GLT_TagsToList( appData.gameListTags );\r
8535             return TRUE;\r
8536 \r
8537         case IDC_GLT_Up:\r
8538             GLT_MoveSelection( hDlg, -1 );\r
8539             return TRUE;\r
8540 \r
8541         case IDC_GLT_Down:\r
8542             GLT_MoveSelection( hDlg, +1 );\r
8543             return TRUE;\r
8544         }\r
8545 \r
8546         break;\r
8547     }\r
8548 \r
8549     return FALSE;\r
8550 }\r
8551 \r
8552 int GameListOptions()\r
8553 {\r
8554     int result;\r
8555     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8556 \r
8557       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8558 \r
8559     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8560 \r
8561     if( result == 0 ) {\r
8562         /* [AS] Memory leak here! */\r
8563         appData.gameListTags = strdup( lpUserGLT ); \r
8564     }\r
8565 \r
8566     return result;\r
8567 }\r
8568 \r
8569 VOID\r
8570 DisplayIcsInteractionTitle(char *str)\r
8571 {\r
8572   char consoleTitle[MSG_SIZ];\r
8573 \r
8574     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8575     SetWindowText(hwndConsole, consoleTitle);\r
8576 \r
8577     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8578       char buf[MSG_SIZ], *p = buf, *q;\r
8579         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8580       do {\r
8581         q = strchr(p, ';');\r
8582         if(q) *q++ = 0;\r
8583         if(*p) ChatPopUp(p);\r
8584       } while(p=q);\r
8585     }\r
8586 \r
8587     SetActiveWindow(hwndMain);\r
8588 }\r
8589 \r
8590 void\r
8591 DrawPosition(int fullRedraw, Board board)\r
8592 {\r
8593   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8594 }\r
8595 \r
8596 void NotifyFrontendLogin()\r
8597 {\r
8598         if (hwndConsole)\r
8599                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8600 }\r
8601 \r
8602 VOID\r
8603 ResetFrontEnd()\r
8604 {\r
8605   fromX = fromY = -1;\r
8606   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8607     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8608     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8609     dragInfo.lastpos = dragInfo.pos;\r
8610     dragInfo.start.x = dragInfo.start.y = -1;\r
8611     dragInfo.from = dragInfo.start;\r
8612     ReleaseCapture();\r
8613     DrawPosition(TRUE, NULL);\r
8614   }\r
8615   TagsPopDown();\r
8616 }\r
8617 \r
8618 \r
8619 VOID\r
8620 CommentPopUp(char *title, char *str)\r
8621 {\r
8622   HWND hwnd = GetActiveWindow();\r
8623   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8624   SAY(str);\r
8625   SetActiveWindow(hwnd);\r
8626 }\r
8627 \r
8628 VOID\r
8629 CommentPopDown(void)\r
8630 {\r
8631   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8632   if (commentDialog) {\r
8633     ShowWindow(commentDialog, SW_HIDE);\r
8634   }\r
8635   commentUp = FALSE;\r
8636 }\r
8637 \r
8638 VOID\r
8639 EditCommentPopUp(int index, char *title, char *str)\r
8640 {\r
8641   EitherCommentPopUp(index, title, str, TRUE);\r
8642 }\r
8643 \r
8644 \r
8645 VOID\r
8646 RingBell()\r
8647 {\r
8648   MyPlaySound(&sounds[(int)SoundMove]);\r
8649 }\r
8650 \r
8651 VOID PlayIcsWinSound()\r
8652 {\r
8653   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8654 }\r
8655 \r
8656 VOID PlayIcsLossSound()\r
8657 {\r
8658   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8659 }\r
8660 \r
8661 VOID PlayIcsDrawSound()\r
8662 {\r
8663   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8664 }\r
8665 \r
8666 VOID PlayIcsUnfinishedSound()\r
8667 {\r
8668   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8669 }\r
8670 \r
8671 VOID\r
8672 PlayAlarmSound()\r
8673 {\r
8674   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8675 }\r
8676 \r
8677 VOID\r
8678 PlayTellSound()\r
8679 {\r
8680   MyPlaySound(&textAttribs[ColorTell].sound);\r
8681 }\r
8682 \r
8683 \r
8684 VOID\r
8685 EchoOn()\r
8686 {\r
8687   HWND hInput;\r
8688   consoleEcho = TRUE;\r
8689   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8690   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8691   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8692 }\r
8693 \r
8694 \r
8695 VOID\r
8696 EchoOff()\r
8697 {\r
8698   CHARFORMAT cf;\r
8699   HWND hInput;\r
8700   consoleEcho = FALSE;\r
8701   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8702   /* This works OK: set text and background both to the same color */\r
8703   cf = consoleCF;\r
8704   cf.crTextColor = COLOR_ECHOOFF;\r
8705   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8706   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8707 }\r
8708 \r
8709 /* No Raw()...? */\r
8710 \r
8711 void Colorize(ColorClass cc, int continuation)\r
8712 {\r
8713   currentColorClass = cc;\r
8714   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8715   consoleCF.crTextColor = textAttribs[cc].color;\r
8716   consoleCF.dwEffects = textAttribs[cc].effects;\r
8717   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8718 }\r
8719 \r
8720 char *\r
8721 UserName()\r
8722 {\r
8723   static char buf[MSG_SIZ];\r
8724   DWORD bufsiz = MSG_SIZ;\r
8725 \r
8726   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8727         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8728   }\r
8729   if (!GetUserName(buf, &bufsiz)) {\r
8730     /*DisplayError("Error getting user name", GetLastError());*/\r
8731     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8732   }\r
8733   return buf;\r
8734 }\r
8735 \r
8736 char *\r
8737 HostName()\r
8738 {\r
8739   static char buf[MSG_SIZ];\r
8740   DWORD bufsiz = MSG_SIZ;\r
8741 \r
8742   if (!GetComputerName(buf, &bufsiz)) {\r
8743     /*DisplayError("Error getting host name", GetLastError());*/\r
8744     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8745   }\r
8746   return buf;\r
8747 }\r
8748 \r
8749 \r
8750 int\r
8751 ClockTimerRunning()\r
8752 {\r
8753   return clockTimerEvent != 0;\r
8754 }\r
8755 \r
8756 int\r
8757 StopClockTimer()\r
8758 {\r
8759   if (clockTimerEvent == 0) return FALSE;\r
8760   KillTimer(hwndMain, clockTimerEvent);\r
8761   clockTimerEvent = 0;\r
8762   return TRUE;\r
8763 }\r
8764 \r
8765 void\r
8766 StartClockTimer(long millisec)\r
8767 {\r
8768   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8769                              (UINT) millisec, NULL);\r
8770 }\r
8771 \r
8772 void\r
8773 DisplayWhiteClock(long timeRemaining, int highlight)\r
8774 {\r
8775   HDC hdc;\r
8776   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8777 \r
8778   if(appData.noGUI) return;\r
8779   hdc = GetDC(hwndMain);\r
8780   if (!IsIconic(hwndMain)) {\r
8781     DisplayAClock(hdc, timeRemaining, highlight, \r
8782                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8783   }\r
8784   if (highlight && iconCurrent == iconBlack) {\r
8785     iconCurrent = iconWhite;\r
8786     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8787     if (IsIconic(hwndMain)) {\r
8788       DrawIcon(hdc, 2, 2, iconCurrent);\r
8789     }\r
8790   }\r
8791   (void) ReleaseDC(hwndMain, hdc);\r
8792   if (hwndConsole)\r
8793     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8794 }\r
8795 \r
8796 void\r
8797 DisplayBlackClock(long timeRemaining, int highlight)\r
8798 {\r
8799   HDC hdc;\r
8800   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8801 \r
8802   if(appData.noGUI) return;\r
8803   hdc = GetDC(hwndMain);\r
8804   if (!IsIconic(hwndMain)) {\r
8805     DisplayAClock(hdc, timeRemaining, highlight, \r
8806                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8807   }\r
8808   if (highlight && iconCurrent == iconWhite) {\r
8809     iconCurrent = iconBlack;\r
8810     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8811     if (IsIconic(hwndMain)) {\r
8812       DrawIcon(hdc, 2, 2, iconCurrent);\r
8813     }\r
8814   }\r
8815   (void) ReleaseDC(hwndMain, hdc);\r
8816   if (hwndConsole)\r
8817     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8818 }\r
8819 \r
8820 \r
8821 int\r
8822 LoadGameTimerRunning()\r
8823 {\r
8824   return loadGameTimerEvent != 0;\r
8825 }\r
8826 \r
8827 int\r
8828 StopLoadGameTimer()\r
8829 {\r
8830   if (loadGameTimerEvent == 0) return FALSE;\r
8831   KillTimer(hwndMain, loadGameTimerEvent);\r
8832   loadGameTimerEvent = 0;\r
8833   return TRUE;\r
8834 }\r
8835 \r
8836 void\r
8837 StartLoadGameTimer(long millisec)\r
8838 {\r
8839   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8840                                 (UINT) millisec, NULL);\r
8841 }\r
8842 \r
8843 void\r
8844 AutoSaveGame()\r
8845 {\r
8846   char *defName;\r
8847   FILE *f;\r
8848   char fileTitle[MSG_SIZ];\r
8849 \r
8850   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8851   f = OpenFileDialog(hwndMain, "a", defName,\r
8852                      appData.oldSaveStyle ? "gam" : "pgn",\r
8853                      GAME_FILT, \r
8854                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8855   if (f != NULL) {\r
8856     SaveGame(f, 0, "");\r
8857     fclose(f);\r
8858   }\r
8859 }\r
8860 \r
8861 \r
8862 void\r
8863 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8864 {\r
8865   if (delayedTimerEvent != 0) {\r
8866     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8867       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8868     }\r
8869     KillTimer(hwndMain, delayedTimerEvent);\r
8870     delayedTimerEvent = 0;\r
8871     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8872     delayedTimerCallback();\r
8873   }\r
8874   delayedTimerCallback = cb;\r
8875   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8876                                 (UINT) millisec, NULL);\r
8877 }\r
8878 \r
8879 DelayedEventCallback\r
8880 GetDelayedEvent()\r
8881 {\r
8882   if (delayedTimerEvent) {\r
8883     return delayedTimerCallback;\r
8884   } else {\r
8885     return NULL;\r
8886   }\r
8887 }\r
8888 \r
8889 void\r
8890 CancelDelayedEvent()\r
8891 {\r
8892   if (delayedTimerEvent) {\r
8893     KillTimer(hwndMain, delayedTimerEvent);\r
8894     delayedTimerEvent = 0;\r
8895   }\r
8896 }\r
8897 \r
8898 DWORD GetWin32Priority(int nice)\r
8899 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8900 /*\r
8901 REALTIME_PRIORITY_CLASS     0x00000100\r
8902 HIGH_PRIORITY_CLASS         0x00000080\r
8903 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8904 NORMAL_PRIORITY_CLASS       0x00000020\r
8905 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8906 IDLE_PRIORITY_CLASS         0x00000040\r
8907 */\r
8908         if (nice < -15) return 0x00000080;\r
8909         if (nice < 0)   return 0x00008000;\r
8910         if (nice == 0)  return 0x00000020;\r
8911         if (nice < 15)  return 0x00004000;\r
8912         return 0x00000040;\r
8913 }\r
8914 \r
8915 void RunCommand(char *cmdLine)\r
8916 {\r
8917   /* Now create the child process. */\r
8918   STARTUPINFO siStartInfo;\r
8919   PROCESS_INFORMATION piProcInfo;\r
8920 \r
8921   siStartInfo.cb = sizeof(STARTUPINFO);\r
8922   siStartInfo.lpReserved = NULL;\r
8923   siStartInfo.lpDesktop = NULL;\r
8924   siStartInfo.lpTitle = NULL;\r
8925   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8926   siStartInfo.cbReserved2 = 0;\r
8927   siStartInfo.lpReserved2 = NULL;\r
8928   siStartInfo.hStdInput = NULL;\r
8929   siStartInfo.hStdOutput = NULL;\r
8930   siStartInfo.hStdError = NULL;\r
8931 \r
8932   CreateProcess(NULL,\r
8933                 cmdLine,           /* command line */\r
8934                 NULL,      /* process security attributes */\r
8935                 NULL,      /* primary thread security attrs */\r
8936                 TRUE,      /* handles are inherited */\r
8937                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8938                 NULL,      /* use parent's environment */\r
8939                 NULL,\r
8940                 &siStartInfo, /* STARTUPINFO pointer */\r
8941                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8942 \r
8943   CloseHandle(piProcInfo.hThread);\r
8944 }\r
8945 \r
8946 /* Start a child process running the given program.\r
8947    The process's standard output can be read from "from", and its\r
8948    standard input can be written to "to".\r
8949    Exit with fatal error if anything goes wrong.\r
8950    Returns an opaque pointer that can be used to destroy the process\r
8951    later.\r
8952 */\r
8953 int\r
8954 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8955 {\r
8956 #define BUFSIZE 4096\r
8957 \r
8958   HANDLE hChildStdinRd, hChildStdinWr,\r
8959     hChildStdoutRd, hChildStdoutWr;\r
8960   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8961   SECURITY_ATTRIBUTES saAttr;\r
8962   BOOL fSuccess;\r
8963   PROCESS_INFORMATION piProcInfo;\r
8964   STARTUPINFO siStartInfo;\r
8965   ChildProc *cp;\r
8966   char buf[MSG_SIZ];\r
8967   DWORD err;\r
8968 \r
8969   if (appData.debugMode) {\r
8970     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8971   }\r
8972 \r
8973   *pr = NoProc;\r
8974 \r
8975   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8976   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8977   saAttr.bInheritHandle = TRUE;\r
8978   saAttr.lpSecurityDescriptor = NULL;\r
8979 \r
8980   /*\r
8981    * The steps for redirecting child's STDOUT:\r
8982    *     1. Create anonymous pipe to be STDOUT for child.\r
8983    *     2. Create a noninheritable duplicate of read handle,\r
8984    *         and close the inheritable read handle.\r
8985    */\r
8986 \r
8987   /* Create a pipe for the child's STDOUT. */\r
8988   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8989     return GetLastError();\r
8990   }\r
8991 \r
8992   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8993   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8994                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8995                              FALSE,     /* not inherited */\r
8996                              DUPLICATE_SAME_ACCESS);\r
8997   if (! fSuccess) {\r
8998     return GetLastError();\r
8999   }\r
9000   CloseHandle(hChildStdoutRd);\r
9001 \r
9002   /*\r
9003    * The steps for redirecting child's STDIN:\r
9004    *     1. Create anonymous pipe to be STDIN for child.\r
9005    *     2. Create a noninheritable duplicate of write handle,\r
9006    *         and close the inheritable write handle.\r
9007    */\r
9008 \r
9009   /* Create a pipe for the child's STDIN. */\r
9010   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9011     return GetLastError();\r
9012   }\r
9013 \r
9014   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9015   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9016                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9017                              FALSE,     /* not inherited */\r
9018                              DUPLICATE_SAME_ACCESS);\r
9019   if (! fSuccess) {\r
9020     return GetLastError();\r
9021   }\r
9022   CloseHandle(hChildStdinWr);\r
9023 \r
9024   /* Arrange to (1) look in dir for the child .exe file, and\r
9025    * (2) have dir be the child's working directory.  Interpret\r
9026    * dir relative to the directory WinBoard loaded from. */\r
9027   GetCurrentDirectory(MSG_SIZ, buf);\r
9028   SetCurrentDirectory(installDir);\r
9029   SetCurrentDirectory(dir);\r
9030 \r
9031   /* Now create the child process. */\r
9032 \r
9033   siStartInfo.cb = sizeof(STARTUPINFO);\r
9034   siStartInfo.lpReserved = NULL;\r
9035   siStartInfo.lpDesktop = NULL;\r
9036   siStartInfo.lpTitle = NULL;\r
9037   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9038   siStartInfo.cbReserved2 = 0;\r
9039   siStartInfo.lpReserved2 = NULL;\r
9040   siStartInfo.hStdInput = hChildStdinRd;\r
9041   siStartInfo.hStdOutput = hChildStdoutWr;\r
9042   siStartInfo.hStdError = hChildStdoutWr;\r
9043 \r
9044   fSuccess = CreateProcess(NULL,\r
9045                            cmdLine,        /* command line */\r
9046                            NULL,           /* process security attributes */\r
9047                            NULL,           /* primary thread security attrs */\r
9048                            TRUE,           /* handles are inherited */\r
9049                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9050                            NULL,           /* use parent's environment */\r
9051                            NULL,\r
9052                            &siStartInfo, /* STARTUPINFO pointer */\r
9053                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9054 \r
9055   err = GetLastError();\r
9056   SetCurrentDirectory(buf); /* return to prev directory */\r
9057   if (! fSuccess) {\r
9058     return err;\r
9059   }\r
9060 \r
9061   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9062     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9063     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9064   }\r
9065 \r
9066   /* Close the handles we don't need in the parent */\r
9067   CloseHandle(piProcInfo.hThread);\r
9068   CloseHandle(hChildStdinRd);\r
9069   CloseHandle(hChildStdoutWr);\r
9070 \r
9071   /* Prepare return value */\r
9072   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9073   cp->kind = CPReal;\r
9074   cp->hProcess = piProcInfo.hProcess;\r
9075   cp->pid = piProcInfo.dwProcessId;\r
9076   cp->hFrom = hChildStdoutRdDup;\r
9077   cp->hTo = hChildStdinWrDup;\r
9078 \r
9079   *pr = (void *) cp;\r
9080 \r
9081   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9082      2000 where engines sometimes don't see the initial command(s)\r
9083      from WinBoard and hang.  I don't understand how that can happen,\r
9084      but the Sleep is harmless, so I've put it in.  Others have also\r
9085      reported what may be the same problem, so hopefully this will fix\r
9086      it for them too.  */\r
9087   Sleep(500);\r
9088 \r
9089   return NO_ERROR;\r
9090 }\r
9091 \r
9092 \r
9093 void\r
9094 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9095 {\r
9096   ChildProc *cp; int result;\r
9097 \r
9098   cp = (ChildProc *) pr;\r
9099   if (cp == NULL) return;\r
9100 \r
9101   switch (cp->kind) {\r
9102   case CPReal:\r
9103     /* TerminateProcess is considered harmful, so... */\r
9104     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9105     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9106     /* The following doesn't work because the chess program\r
9107        doesn't "have the same console" as WinBoard.  Maybe\r
9108        we could arrange for this even though neither WinBoard\r
9109        nor the chess program uses a console for stdio? */\r
9110     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9111 \r
9112     /* [AS] Special termination modes for misbehaving programs... */\r
9113     if( signal == 9 ) { \r
9114         result = TerminateProcess( cp->hProcess, 0 );\r
9115 \r
9116         if ( appData.debugMode) {\r
9117             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9118         }\r
9119     }\r
9120     else if( signal == 10 ) {\r
9121         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9122 \r
9123         if( dw != WAIT_OBJECT_0 ) {\r
9124             result = TerminateProcess( cp->hProcess, 0 );\r
9125 \r
9126             if ( appData.debugMode) {\r
9127                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9128             }\r
9129 \r
9130         }\r
9131     }\r
9132 \r
9133     CloseHandle(cp->hProcess);\r
9134     break;\r
9135 \r
9136   case CPComm:\r
9137     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9138     break;\r
9139 \r
9140   case CPSock:\r
9141     closesocket(cp->sock);\r
9142     WSACleanup();\r
9143     break;\r
9144 \r
9145   case CPRcmd:\r
9146     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9147     closesocket(cp->sock);\r
9148     closesocket(cp->sock2);\r
9149     WSACleanup();\r
9150     break;\r
9151   }\r
9152   free(cp);\r
9153 }\r
9154 \r
9155 void\r
9156 InterruptChildProcess(ProcRef pr)\r
9157 {\r
9158   ChildProc *cp;\r
9159 \r
9160   cp = (ChildProc *) pr;\r
9161   if (cp == NULL) return;\r
9162   switch (cp->kind) {\r
9163   case CPReal:\r
9164     /* The following doesn't work because the chess program\r
9165        doesn't "have the same console" as WinBoard.  Maybe\r
9166        we could arrange for this even though neither WinBoard\r
9167        nor the chess program uses a console for stdio */\r
9168     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9169     break;\r
9170 \r
9171   case CPComm:\r
9172   case CPSock:\r
9173     /* Can't interrupt */\r
9174     break;\r
9175 \r
9176   case CPRcmd:\r
9177     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9178     break;\r
9179   }\r
9180 }\r
9181 \r
9182 \r
9183 int\r
9184 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9185 {\r
9186   char cmdLine[MSG_SIZ];\r
9187 \r
9188   if (port[0] == NULLCHAR) {\r
9189     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9190   } else {\r
9191     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9192   }\r
9193   return StartChildProcess(cmdLine, "", pr);\r
9194 }\r
9195 \r
9196 \r
9197 /* Code to open TCP sockets */\r
9198 \r
9199 int\r
9200 OpenTCP(char *host, char *port, ProcRef *pr)\r
9201 {\r
9202   ChildProc *cp;\r
9203   int err;\r
9204   SOCKET s;\r
9205   struct sockaddr_in sa, mysa;\r
9206   struct hostent FAR *hp;\r
9207   unsigned short uport;\r
9208   WORD wVersionRequested;\r
9209   WSADATA wsaData;\r
9210 \r
9211   /* Initialize socket DLL */\r
9212   wVersionRequested = MAKEWORD(1, 1);\r
9213   err = WSAStartup(wVersionRequested, &wsaData);\r
9214   if (err != 0) return err;\r
9215 \r
9216   /* Make socket */\r
9217   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9218     err = WSAGetLastError();\r
9219     WSACleanup();\r
9220     return err;\r
9221   }\r
9222 \r
9223   /* Bind local address using (mostly) don't-care values.\r
9224    */\r
9225   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9226   mysa.sin_family = AF_INET;\r
9227   mysa.sin_addr.s_addr = INADDR_ANY;\r
9228   uport = (unsigned short) 0;\r
9229   mysa.sin_port = htons(uport);\r
9230   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9231       == SOCKET_ERROR) {\r
9232     err = WSAGetLastError();\r
9233     WSACleanup();\r
9234     return err;\r
9235   }\r
9236 \r
9237   /* Resolve remote host name */\r
9238   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9239   if (!(hp = gethostbyname(host))) {\r
9240     unsigned int b0, b1, b2, b3;\r
9241 \r
9242     err = WSAGetLastError();\r
9243 \r
9244     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9245       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9246       hp->h_addrtype = AF_INET;\r
9247       hp->h_length = 4;\r
9248       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9249       hp->h_addr_list[0] = (char *) malloc(4);\r
9250       hp->h_addr_list[0][0] = (char) b0;\r
9251       hp->h_addr_list[0][1] = (char) b1;\r
9252       hp->h_addr_list[0][2] = (char) b2;\r
9253       hp->h_addr_list[0][3] = (char) b3;\r
9254     } else {\r
9255       WSACleanup();\r
9256       return err;\r
9257     }\r
9258   }\r
9259   sa.sin_family = hp->h_addrtype;\r
9260   uport = (unsigned short) atoi(port);\r
9261   sa.sin_port = htons(uport);\r
9262   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9263 \r
9264   /* Make connection */\r
9265   if (connect(s, (struct sockaddr *) &sa,\r
9266               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9267     err = WSAGetLastError();\r
9268     WSACleanup();\r
9269     return err;\r
9270   }\r
9271 \r
9272   /* Prepare return value */\r
9273   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9274   cp->kind = CPSock;\r
9275   cp->sock = s;\r
9276   *pr = (ProcRef *) cp;\r
9277 \r
9278   return NO_ERROR;\r
9279 }\r
9280 \r
9281 int\r
9282 OpenCommPort(char *name, ProcRef *pr)\r
9283 {\r
9284   HANDLE h;\r
9285   COMMTIMEOUTS ct;\r
9286   ChildProc *cp;\r
9287   char fullname[MSG_SIZ];\r
9288 \r
9289   if (*name != '\\')\r
9290     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9291   else\r
9292     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9293 \r
9294   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9295                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9296   if (h == (HANDLE) -1) {\r
9297     return GetLastError();\r
9298   }\r
9299   hCommPort = h;\r
9300 \r
9301   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9302 \r
9303   /* Accumulate characters until a 100ms pause, then parse */\r
9304   ct.ReadIntervalTimeout = 100;\r
9305   ct.ReadTotalTimeoutMultiplier = 0;\r
9306   ct.ReadTotalTimeoutConstant = 0;\r
9307   ct.WriteTotalTimeoutMultiplier = 0;\r
9308   ct.WriteTotalTimeoutConstant = 0;\r
9309   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9310 \r
9311   /* Prepare return value */\r
9312   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9313   cp->kind = CPComm;\r
9314   cp->hFrom = h;\r
9315   cp->hTo = h;\r
9316   *pr = (ProcRef *) cp;\r
9317 \r
9318   return NO_ERROR;\r
9319 }\r
9320 \r
9321 int\r
9322 OpenLoopback(ProcRef *pr)\r
9323 {\r
9324   DisplayFatalError(_("Not implemented"), 0, 1);\r
9325   return NO_ERROR;\r
9326 }\r
9327 \r
9328 \r
9329 int\r
9330 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9331 {\r
9332   ChildProc *cp;\r
9333   int err;\r
9334   SOCKET s, s2, s3;\r
9335   struct sockaddr_in sa, mysa;\r
9336   struct hostent FAR *hp;\r
9337   unsigned short uport;\r
9338   WORD wVersionRequested;\r
9339   WSADATA wsaData;\r
9340   int fromPort;\r
9341   char stderrPortStr[MSG_SIZ];\r
9342 \r
9343   /* Initialize socket DLL */\r
9344   wVersionRequested = MAKEWORD(1, 1);\r
9345   err = WSAStartup(wVersionRequested, &wsaData);\r
9346   if (err != 0) return err;\r
9347 \r
9348   /* Resolve remote host name */\r
9349   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9350   if (!(hp = gethostbyname(host))) {\r
9351     unsigned int b0, b1, b2, b3;\r
9352 \r
9353     err = WSAGetLastError();\r
9354 \r
9355     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9356       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9357       hp->h_addrtype = AF_INET;\r
9358       hp->h_length = 4;\r
9359       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9360       hp->h_addr_list[0] = (char *) malloc(4);\r
9361       hp->h_addr_list[0][0] = (char) b0;\r
9362       hp->h_addr_list[0][1] = (char) b1;\r
9363       hp->h_addr_list[0][2] = (char) b2;\r
9364       hp->h_addr_list[0][3] = (char) b3;\r
9365     } else {\r
9366       WSACleanup();\r
9367       return err;\r
9368     }\r
9369   }\r
9370   sa.sin_family = hp->h_addrtype;\r
9371   uport = (unsigned short) 514;\r
9372   sa.sin_port = htons(uport);\r
9373   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9374 \r
9375   /* Bind local socket to unused "privileged" port address\r
9376    */\r
9377   s = INVALID_SOCKET;\r
9378   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9379   mysa.sin_family = AF_INET;\r
9380   mysa.sin_addr.s_addr = INADDR_ANY;\r
9381   for (fromPort = 1023;; fromPort--) {\r
9382     if (fromPort < 0) {\r
9383       WSACleanup();\r
9384       return WSAEADDRINUSE;\r
9385     }\r
9386     if (s == INVALID_SOCKET) {\r
9387       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9388         err = WSAGetLastError();\r
9389         WSACleanup();\r
9390         return err;\r
9391       }\r
9392     }\r
9393     uport = (unsigned short) fromPort;\r
9394     mysa.sin_port = htons(uport);\r
9395     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9396         == SOCKET_ERROR) {\r
9397       err = WSAGetLastError();\r
9398       if (err == WSAEADDRINUSE) continue;\r
9399       WSACleanup();\r
9400       return err;\r
9401     }\r
9402     if (connect(s, (struct sockaddr *) &sa,\r
9403       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9404       err = WSAGetLastError();\r
9405       if (err == WSAEADDRINUSE) {\r
9406         closesocket(s);\r
9407         s = -1;\r
9408         continue;\r
9409       }\r
9410       WSACleanup();\r
9411       return err;\r
9412     }\r
9413     break;\r
9414   }\r
9415 \r
9416   /* Bind stderr local socket to unused "privileged" port address\r
9417    */\r
9418   s2 = INVALID_SOCKET;\r
9419   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9420   mysa.sin_family = AF_INET;\r
9421   mysa.sin_addr.s_addr = INADDR_ANY;\r
9422   for (fromPort = 1023;; fromPort--) {\r
9423     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9424     if (fromPort < 0) {\r
9425       (void) closesocket(s);\r
9426       WSACleanup();\r
9427       return WSAEADDRINUSE;\r
9428     }\r
9429     if (s2 == INVALID_SOCKET) {\r
9430       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9431         err = WSAGetLastError();\r
9432         closesocket(s);\r
9433         WSACleanup();\r
9434         return err;\r
9435       }\r
9436     }\r
9437     uport = (unsigned short) fromPort;\r
9438     mysa.sin_port = htons(uport);\r
9439     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9440         == SOCKET_ERROR) {\r
9441       err = WSAGetLastError();\r
9442       if (err == WSAEADDRINUSE) continue;\r
9443       (void) closesocket(s);\r
9444       WSACleanup();\r
9445       return err;\r
9446     }\r
9447     if (listen(s2, 1) == SOCKET_ERROR) {\r
9448       err = WSAGetLastError();\r
9449       if (err == WSAEADDRINUSE) {\r
9450         closesocket(s2);\r
9451         s2 = INVALID_SOCKET;\r
9452         continue;\r
9453       }\r
9454       (void) closesocket(s);\r
9455       (void) closesocket(s2);\r
9456       WSACleanup();\r
9457       return err;\r
9458     }\r
9459     break;\r
9460   }\r
9461   prevStderrPort = fromPort; // remember port used\r
9462   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9463 \r
9464   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9465     err = WSAGetLastError();\r
9466     (void) closesocket(s);\r
9467     (void) closesocket(s2);\r
9468     WSACleanup();\r
9469     return err;\r
9470   }\r
9471 \r
9472   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9473     err = WSAGetLastError();\r
9474     (void) closesocket(s);\r
9475     (void) closesocket(s2);\r
9476     WSACleanup();\r
9477     return err;\r
9478   }\r
9479   if (*user == NULLCHAR) user = UserName();\r
9480   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9481     err = WSAGetLastError();\r
9482     (void) closesocket(s);\r
9483     (void) closesocket(s2);\r
9484     WSACleanup();\r
9485     return err;\r
9486   }\r
9487   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9488     err = WSAGetLastError();\r
9489     (void) closesocket(s);\r
9490     (void) closesocket(s2);\r
9491     WSACleanup();\r
9492     return err;\r
9493   }\r
9494 \r
9495   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9496     err = WSAGetLastError();\r
9497     (void) closesocket(s);\r
9498     (void) closesocket(s2);\r
9499     WSACleanup();\r
9500     return err;\r
9501   }\r
9502   (void) closesocket(s2);  /* Stop listening */\r
9503 \r
9504   /* Prepare return value */\r
9505   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9506   cp->kind = CPRcmd;\r
9507   cp->sock = s;\r
9508   cp->sock2 = s3;\r
9509   *pr = (ProcRef *) cp;\r
9510 \r
9511   return NO_ERROR;\r
9512 }\r
9513 \r
9514 \r
9515 InputSourceRef\r
9516 AddInputSource(ProcRef pr, int lineByLine,\r
9517                InputCallback func, VOIDSTAR closure)\r
9518 {\r
9519   InputSource *is, *is2 = NULL;\r
9520   ChildProc *cp = (ChildProc *) pr;\r
9521 \r
9522   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9523   is->lineByLine = lineByLine;\r
9524   is->func = func;\r
9525   is->closure = closure;\r
9526   is->second = NULL;\r
9527   is->next = is->buf;\r
9528   if (pr == NoProc) {\r
9529     is->kind = CPReal;\r
9530     consoleInputSource = is;\r
9531   } else {\r
9532     is->kind = cp->kind;\r
9533     /* \r
9534         [AS] Try to avoid a race condition if the thread is given control too early:\r
9535         we create all threads suspended so that the is->hThread variable can be\r
9536         safely assigned, then let the threads start with ResumeThread.\r
9537     */\r
9538     switch (cp->kind) {\r
9539     case CPReal:\r
9540       is->hFile = cp->hFrom;\r
9541       cp->hFrom = NULL; /* now owned by InputThread */\r
9542       is->hThread =\r
9543         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9544                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9545       break;\r
9546 \r
9547     case CPComm:\r
9548       is->hFile = cp->hFrom;\r
9549       cp->hFrom = NULL; /* now owned by InputThread */\r
9550       is->hThread =\r
9551         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9552                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9553       break;\r
9554 \r
9555     case CPSock:\r
9556       is->sock = cp->sock;\r
9557       is->hThread =\r
9558         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9559                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9560       break;\r
9561 \r
9562     case CPRcmd:\r
9563       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9564       *is2 = *is;\r
9565       is->sock = cp->sock;\r
9566       is->second = is2;\r
9567       is2->sock = cp->sock2;\r
9568       is2->second = is2;\r
9569       is->hThread =\r
9570         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9571                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9572       is2->hThread =\r
9573         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9574                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9575       break;\r
9576     }\r
9577 \r
9578     if( is->hThread != NULL ) {\r
9579         ResumeThread( is->hThread );\r
9580     }\r
9581 \r
9582     if( is2 != NULL && is2->hThread != NULL ) {\r
9583         ResumeThread( is2->hThread );\r
9584     }\r
9585   }\r
9586 \r
9587   return (InputSourceRef) is;\r
9588 }\r
9589 \r
9590 void\r
9591 RemoveInputSource(InputSourceRef isr)\r
9592 {\r
9593   InputSource *is;\r
9594 \r
9595   is = (InputSource *) isr;\r
9596   is->hThread = NULL;  /* tell thread to stop */\r
9597   CloseHandle(is->hThread);\r
9598   if (is->second != NULL) {\r
9599     is->second->hThread = NULL;\r
9600     CloseHandle(is->second->hThread);\r
9601   }\r
9602 }\r
9603 \r
9604 int no_wrap(char *message, int count)\r
9605 {\r
9606     ConsoleOutput(message, count, FALSE);\r
9607     return count;\r
9608 }\r
9609 \r
9610 int\r
9611 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9612 {\r
9613   DWORD dOutCount;\r
9614   int outCount = SOCKET_ERROR;\r
9615   ChildProc *cp = (ChildProc *) pr;\r
9616   static OVERLAPPED ovl;\r
9617   static int line = 0;\r
9618 \r
9619   if (pr == NoProc)\r
9620   {\r
9621     if (appData.noJoin || !appData.useInternalWrap)\r
9622       return no_wrap(message, count);\r
9623     else\r
9624     {\r
9625       int width = get_term_width();\r
9626       int len = wrap(NULL, message, count, width, &line);\r
9627       char *msg = malloc(len);\r
9628       int dbgchk;\r
9629 \r
9630       if (!msg)\r
9631         return no_wrap(message, count);\r
9632       else\r
9633       {\r
9634         dbgchk = wrap(msg, message, count, width, &line);\r
9635         if (dbgchk != len && appData.debugMode)\r
9636             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9637         ConsoleOutput(msg, len, FALSE);\r
9638         free(msg);\r
9639         return len;\r
9640       }\r
9641     }\r
9642   }\r
9643 \r
9644   if (ovl.hEvent == NULL) {\r
9645     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9646   }\r
9647   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9648 \r
9649   switch (cp->kind) {\r
9650   case CPSock:\r
9651   case CPRcmd:\r
9652     outCount = send(cp->sock, message, count, 0);\r
9653     if (outCount == SOCKET_ERROR) {\r
9654       *outError = WSAGetLastError();\r
9655     } else {\r
9656       *outError = NO_ERROR;\r
9657     }\r
9658     break;\r
9659 \r
9660   case CPReal:\r
9661     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9662                   &dOutCount, NULL)) {\r
9663       *outError = NO_ERROR;\r
9664       outCount = (int) dOutCount;\r
9665     } else {\r
9666       *outError = GetLastError();\r
9667     }\r
9668     break;\r
9669 \r
9670   case CPComm:\r
9671     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9672                             &dOutCount, &ovl);\r
9673     if (*outError == NO_ERROR) {\r
9674       outCount = (int) dOutCount;\r
9675     }\r
9676     break;\r
9677   }\r
9678   return outCount;\r
9679 }\r
9680 \r
9681 void\r
9682 DoSleep(int n)\r
9683 {\r
9684     if(n != 0) Sleep(n);\r
9685 }\r
9686 \r
9687 int\r
9688 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9689                        long msdelay)\r
9690 {\r
9691   /* Ignore delay, not implemented for WinBoard */\r
9692   return OutputToProcess(pr, message, count, outError);\r
9693 }\r
9694 \r
9695 \r
9696 void\r
9697 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9698                         char *buf, int count, int error)\r
9699 {\r
9700   DisplayFatalError(_("Not implemented"), 0, 1);\r
9701 }\r
9702 \r
9703 /* see wgamelist.c for Game List functions */\r
9704 /* see wedittags.c for Edit Tags functions */\r
9705 \r
9706 \r
9707 VOID\r
9708 ICSInitScript()\r
9709 {\r
9710   FILE *f;\r
9711   char buf[MSG_SIZ];\r
9712   char *dummy;\r
9713 \r
9714   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9715     f = fopen(buf, "r");\r
9716     if (f != NULL) {\r
9717       ProcessICSInitScript(f);\r
9718       fclose(f);\r
9719     }\r
9720   }\r
9721 }\r
9722 \r
9723 \r
9724 VOID\r
9725 StartAnalysisClock()\r
9726 {\r
9727   if (analysisTimerEvent) return;\r
9728   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9729                                         (UINT) 2000, NULL);\r
9730 }\r
9731 \r
9732 VOID\r
9733 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9734 {\r
9735   highlightInfo.sq[0].x = fromX;\r
9736   highlightInfo.sq[0].y = fromY;\r
9737   highlightInfo.sq[1].x = toX;\r
9738   highlightInfo.sq[1].y = toY;\r
9739 }\r
9740 \r
9741 VOID\r
9742 ClearHighlights()\r
9743 {\r
9744   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9745     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9746 }\r
9747 \r
9748 VOID\r
9749 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9750 {\r
9751   premoveHighlightInfo.sq[0].x = fromX;\r
9752   premoveHighlightInfo.sq[0].y = fromY;\r
9753   premoveHighlightInfo.sq[1].x = toX;\r
9754   premoveHighlightInfo.sq[1].y = toY;\r
9755 }\r
9756 \r
9757 VOID\r
9758 ClearPremoveHighlights()\r
9759 {\r
9760   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9761     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9762 }\r
9763 \r
9764 VOID\r
9765 ShutDownFrontEnd()\r
9766 {\r
9767   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9768   DeleteClipboardTempFiles();\r
9769 }\r
9770 \r
9771 void\r
9772 BoardToTop()\r
9773 {\r
9774     if (IsIconic(hwndMain))\r
9775       ShowWindow(hwndMain, SW_RESTORE);\r
9776 \r
9777     SetActiveWindow(hwndMain);\r
9778 }\r
9779 \r
9780 /*\r
9781  * Prototypes for animation support routines\r
9782  */\r
9783 static void ScreenSquare(int column, int row, POINT * pt);\r
9784 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9785      POINT frames[], int * nFrames);\r
9786 \r
9787 \r
9788 #define kFactor 4\r
9789 \r
9790 void\r
9791 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9792 {       // [HGM] atomic: animate blast wave\r
9793         int i;\r
9794 \r
9795         explodeInfo.fromX = fromX;\r
9796         explodeInfo.fromY = fromY;\r
9797         explodeInfo.toX = toX;\r
9798         explodeInfo.toY = toY;\r
9799         for(i=1; i<4*kFactor; i++) {\r
9800             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9801             DrawPosition(FALSE, board);\r
9802             Sleep(appData.animSpeed);\r
9803         }\r
9804         explodeInfo.radius = 0;\r
9805         DrawPosition(TRUE, board);\r
9806 }\r
9807 \r
9808 void\r
9809 AnimateMove(board, fromX, fromY, toX, toY)\r
9810      Board board;\r
9811      int fromX;\r
9812      int fromY;\r
9813      int toX;\r
9814      int toY;\r
9815 {\r
9816   ChessSquare piece;\r
9817   POINT start, finish, mid;\r
9818   POINT frames[kFactor * 2 + 1];\r
9819   int nFrames, n;\r
9820 \r
9821   if (!appData.animate) return;\r
9822   if (doingSizing) return;\r
9823   if (fromY < 0 || fromX < 0) return;\r
9824   piece = board[fromY][fromX];\r
9825   if (piece >= EmptySquare) return;\r
9826 \r
9827   ScreenSquare(fromX, fromY, &start);\r
9828   ScreenSquare(toX, toY, &finish);\r
9829 \r
9830   /* All moves except knight jumps move in straight line */\r
9831   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9832     mid.x = start.x + (finish.x - start.x) / 2;\r
9833     mid.y = start.y + (finish.y - start.y) / 2;\r
9834   } else {\r
9835     /* Knight: make straight movement then diagonal */\r
9836     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9837        mid.x = start.x + (finish.x - start.x) / 2;\r
9838        mid.y = start.y;\r
9839      } else {\r
9840        mid.x = start.x;\r
9841        mid.y = start.y + (finish.y - start.y) / 2;\r
9842      }\r
9843   }\r
9844   \r
9845   /* Don't use as many frames for very short moves */\r
9846   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9847     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9848   else\r
9849     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9850 \r
9851   animInfo.from.x = fromX;\r
9852   animInfo.from.y = fromY;\r
9853   animInfo.to.x = toX;\r
9854   animInfo.to.y = toY;\r
9855   animInfo.lastpos = start;\r
9856   animInfo.piece = piece;\r
9857   for (n = 0; n < nFrames; n++) {\r
9858     animInfo.pos = frames[n];\r
9859     DrawPosition(FALSE, NULL);\r
9860     animInfo.lastpos = animInfo.pos;\r
9861     Sleep(appData.animSpeed);\r
9862   }\r
9863   animInfo.pos = finish;\r
9864   DrawPosition(FALSE, NULL);\r
9865   animInfo.piece = EmptySquare;\r
9866   Explode(board, fromX, fromY, toX, toY);\r
9867 }\r
9868 \r
9869 /*      Convert board position to corner of screen rect and color       */\r
9870 \r
9871 static void\r
9872 ScreenSquare(column, row, pt)\r
9873      int column; int row; POINT * pt;\r
9874 {\r
9875   if (flipView) {\r
9876     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9877     pt->y = lineGap + row * (squareSize + lineGap);\r
9878   } else {\r
9879     pt->x = lineGap + column * (squareSize + lineGap);\r
9880     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9881   }\r
9882 }\r
9883 \r
9884 /*      Generate a series of frame coords from start->mid->finish.\r
9885         The movement rate doubles until the half way point is\r
9886         reached, then halves back down to the final destination,\r
9887         which gives a nice slow in/out effect. The algorithmn\r
9888         may seem to generate too many intermediates for short\r
9889         moves, but remember that the purpose is to attract the\r
9890         viewers attention to the piece about to be moved and\r
9891         then to where it ends up. Too few frames would be less\r
9892         noticeable.                                             */\r
9893 \r
9894 static void\r
9895 Tween(start, mid, finish, factor, frames, nFrames)\r
9896      POINT * start; POINT * mid;\r
9897      POINT * finish; int factor;\r
9898      POINT frames[]; int * nFrames;\r
9899 {\r
9900   int n, fraction = 1, count = 0;\r
9901 \r
9902   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9903   for (n = 0; n < factor; n++)\r
9904     fraction *= 2;\r
9905   for (n = 0; n < factor; n++) {\r
9906     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9907     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9908     count ++;\r
9909     fraction = fraction / 2;\r
9910   }\r
9911   \r
9912   /* Midpoint */\r
9913   frames[count] = *mid;\r
9914   count ++;\r
9915   \r
9916   /* Slow out, stepping 1/2, then 1/4, ... */\r
9917   fraction = 2;\r
9918   for (n = 0; n < factor; n++) {\r
9919     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9920     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9921     count ++;\r
9922     fraction = fraction * 2;\r
9923   }\r
9924   *nFrames = count;\r
9925 }\r
9926 \r
9927 void\r
9928 SettingsPopUp(ChessProgramState *cps)\r
9929 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9930       EngineOptionsPopup(savedHwnd, cps);\r
9931 }\r
9932 \r
9933 int flock(int fid, int code)\r
9934 {\r
9935     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9936     OVERLAPPED ov;\r
9937     ov.hEvent = NULL;\r
9938     ov.Offset = 0;\r
9939     ov.OffsetHigh = 0;\r
9940     switch(code) {\r
9941       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9942       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9943       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9944       default: return -1;\r
9945     }\r
9946     return 0;\r
9947 }\r