Give the dual-board option a separate board window
[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.icsActive) return;\r
501     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
502         HMENU mainMenu = GetMenu(hwndMain);\r
503         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
504         int i=IDM_RecentEngines;\r
505         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
506         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
507         while(*s) {\r
508           char *p = strchr(s, '\n');\r
509           if(p == NULL) return; // malformed!\r
510           *p = NULLCHAR;\r
511           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
512           *p = '\n';\r
513           s = p+1;\r
514         }\r
515     }\r
516 }\r
517 \r
518 \r
519 typedef struct {\r
520   char *name;\r
521   int squareSize;\r
522   int lineGap;\r
523   int smallLayout;\r
524   int tinyLayout;\r
525   int cliWidth, cliHeight;\r
526 } SizeInfo;\r
527 \r
528 SizeInfo sizeInfo[] = \r
529 {\r
530   { "tiny",     21, 0, 1, 1, 0, 0 },\r
531   { "teeny",    25, 1, 1, 1, 0, 0 },\r
532   { "dinky",    29, 1, 1, 1, 0, 0 },\r
533   { "petite",   33, 1, 1, 1, 0, 0 },\r
534   { "slim",     37, 2, 1, 0, 0, 0 },\r
535   { "small",    40, 2, 1, 0, 0, 0 },\r
536   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
537   { "middling", 49, 2, 0, 0, 0, 0 },\r
538   { "average",  54, 2, 0, 0, 0, 0 },\r
539   { "moderate", 58, 3, 0, 0, 0, 0 },\r
540   { "medium",   64, 3, 0, 0, 0, 0 },\r
541   { "bulky",    72, 3, 0, 0, 0, 0 },\r
542   { "large",    80, 3, 0, 0, 0, 0 },\r
543   { "big",      87, 3, 0, 0, 0, 0 },\r
544   { "huge",     95, 3, 0, 0, 0, 0 },\r
545   { "giant",    108, 3, 0, 0, 0, 0 },\r
546   { "colossal", 116, 4, 0, 0, 0, 0 },\r
547   { "titanic",  129, 4, 0, 0, 0, 0 },\r
548   { NULL, 0, 0, 0, 0, 0, 0 }\r
549 };\r
550 \r
551 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
552 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
553 {\r
554   { 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
555   { 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
556   { 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
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572 };\r
573 \r
574 MyFont *font[NUM_SIZES][NUM_FONTS];\r
575 \r
576 typedef struct {\r
577   char *label;\r
578   int id;\r
579   HWND hwnd;\r
580   WNDPROC wndproc;\r
581 } MyButtonDesc;\r
582 \r
583 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
584 #define N_BUTTONS 5\r
585 \r
586 MyButtonDesc buttonDesc[N_BUTTONS] =\r
587 {\r
588   {"<<", IDM_ToStart, NULL, NULL},\r
589   {"<", IDM_Backward, NULL, NULL},\r
590   {"P", IDM_Pause, NULL, NULL},\r
591   {">", IDM_Forward, NULL, NULL},\r
592   {">>", IDM_ToEnd, NULL, NULL},\r
593 };\r
594 \r
595 int tinyLayout = 0, smallLayout = 0;\r
596 #define MENU_BAR_ITEMS 9\r
597 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
598   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
599   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
600 };\r
601 \r
602 \r
603 MySound sounds[(int)NSoundClasses];\r
604 MyTextAttribs textAttribs[(int)NColorClasses];\r
605 \r
606 MyColorizeAttribs colorizeAttribs[] = {\r
607   { (COLORREF)0, 0, N_("Shout Text") },\r
608   { (COLORREF)0, 0, N_("SShout/CShout") },\r
609   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
610   { (COLORREF)0, 0, N_("Channel Text") },\r
611   { (COLORREF)0, 0, N_("Kibitz Text") },\r
612   { (COLORREF)0, 0, N_("Tell Text") },\r
613   { (COLORREF)0, 0, N_("Challenge Text") },\r
614   { (COLORREF)0, 0, N_("Request Text") },\r
615   { (COLORREF)0, 0, N_("Seek Text") },\r
616   { (COLORREF)0, 0, N_("Normal Text") },\r
617   { (COLORREF)0, 0, N_("None") }\r
618 };\r
619 \r
620 \r
621 \r
622 static char *commentTitle;\r
623 static char *commentText;\r
624 static int commentIndex;\r
625 static Boolean editComment = FALSE;\r
626 \r
627 \r
628 char errorTitle[MSG_SIZ];\r
629 char errorMessage[2*MSG_SIZ];\r
630 HWND errorDialog = NULL;\r
631 BOOLEAN moveErrorMessageUp = FALSE;\r
632 BOOLEAN consoleEcho = TRUE;\r
633 CHARFORMAT consoleCF;\r
634 COLORREF consoleBackgroundColor;\r
635 \r
636 char *programVersion;\r
637 \r
638 #define CPReal 1\r
639 #define CPComm 2\r
640 #define CPSock 3\r
641 #define CPRcmd 4\r
642 typedef int CPKind;\r
643 \r
644 typedef struct {\r
645   CPKind kind;\r
646   HANDLE hProcess;\r
647   DWORD pid;\r
648   HANDLE hTo;\r
649   HANDLE hFrom;\r
650   SOCKET sock;\r
651   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
652 } ChildProc;\r
653 \r
654 #define INPUT_SOURCE_BUF_SIZE 4096\r
655 \r
656 typedef struct _InputSource {\r
657   CPKind kind;\r
658   HANDLE hFile;\r
659   SOCKET sock;\r
660   int lineByLine;\r
661   HANDLE hThread;\r
662   DWORD id;\r
663   char buf[INPUT_SOURCE_BUF_SIZE];\r
664   char *next;\r
665   DWORD count;\r
666   int error;\r
667   InputCallback func;\r
668   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
669   VOIDSTAR closure;\r
670 } InputSource;\r
671 \r
672 InputSource *consoleInputSource;\r
673 \r
674 DCB dcb;\r
675 \r
676 /* forward */\r
677 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
678 VOID ConsoleCreate();\r
679 LRESULT CALLBACK\r
680   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
681 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
682 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
683 VOID ParseCommSettings(char *arg, DCB *dcb);\r
684 LRESULT CALLBACK\r
685   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
686 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
687 void ParseIcsTextMenu(char *icsTextMenuString);\r
688 VOID PopUpNameDialog(char firstchar);\r
689 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
690 \r
691 /* [AS] */\r
692 int NewGameFRC();\r
693 int GameListOptions();\r
694 \r
695 int dummy; // [HGM] for obsolete args\r
696 \r
697 HWND hwndMain = NULL;        /* root window*/\r
698 HWND hwndConsole = NULL;\r
699 HWND commentDialog = NULL;\r
700 HWND moveHistoryDialog = NULL;\r
701 HWND evalGraphDialog = NULL;\r
702 HWND engineOutputDialog = NULL;\r
703 HWND gameListDialog = NULL;\r
704 HWND editTagsDialog = NULL;\r
705 \r
706 int commentUp = FALSE;\r
707 \r
708 WindowPlacement wpMain;\r
709 WindowPlacement wpConsole;\r
710 WindowPlacement wpComment;\r
711 WindowPlacement wpMoveHistory;\r
712 WindowPlacement wpEvalGraph;\r
713 WindowPlacement wpEngineOutput;\r
714 WindowPlacement wpGameList;\r
715 WindowPlacement wpTags;\r
716 \r
717 VOID EngineOptionsPopup(); // [HGM] settings\r
718 \r
719 VOID GothicPopUp(char *title, VariantClass variant);\r
720 /*\r
721  * Setting "frozen" should disable all user input other than deleting\r
722  * the window.  We do this while engines are initializing themselves.\r
723  */\r
724 static int frozen = 0;\r
725 static int oldMenuItemState[MENU_BAR_ITEMS];\r
726 void FreezeUI()\r
727 {\r
728   HMENU hmenu;\r
729   int i;\r
730 \r
731   if (frozen) return;\r
732   frozen = 1;\r
733   hmenu = GetMenu(hwndMain);\r
734   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
735     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
736   }\r
737   DrawMenuBar(hwndMain);\r
738 }\r
739 \r
740 /* Undo a FreezeUI */\r
741 void ThawUI()\r
742 {\r
743   HMENU hmenu;\r
744   int i;\r
745 \r
746   if (!frozen) return;\r
747   frozen = 0;\r
748   hmenu = GetMenu(hwndMain);\r
749   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
750     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
751   }\r
752   DrawMenuBar(hwndMain);\r
753 }\r
754 \r
755 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
756 \r
757 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
758 #ifdef JAWS\r
759 #include "jaws.c"\r
760 #else\r
761 #define JAWS_INIT\r
762 #define JAWS_ARGS\r
763 #define JAWS_ALT_INTERCEPT\r
764 #define JAWS_KBUP_NAVIGATION\r
765 #define JAWS_KBDOWN_NAVIGATION\r
766 #define JAWS_MENU_ITEMS\r
767 #define JAWS_SILENCE\r
768 #define JAWS_REPLAY\r
769 #define JAWS_ACCEL\r
770 #define JAWS_COPYRIGHT\r
771 #define JAWS_DELETE(X) X\r
772 #define SAYMACHINEMOVE()\r
773 #define SAY(X)\r
774 #endif\r
775 \r
776 /*---------------------------------------------------------------------------*\\r
777  *\r
778  * WinMain\r
779  *\r
780 \*---------------------------------------------------------------------------*/\r
781 \r
782 int APIENTRY\r
783 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
784         LPSTR lpCmdLine, int nCmdShow)\r
785 {\r
786   MSG msg;\r
787   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
788 //  INITCOMMONCONTROLSEX ex;\r
789 \r
790   debugFP = stderr;\r
791 \r
792   LoadLibrary("RICHED32.DLL");\r
793   consoleCF.cbSize = sizeof(CHARFORMAT);\r
794 \r
795   if (!InitApplication(hInstance)) {\r
796     return (FALSE);\r
797   }\r
798   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
799     return (FALSE);\r
800   }\r
801 \r
802   JAWS_INIT\r
803   TranslateMenus(1);\r
804 \r
805 //  InitCommonControlsEx(&ex);\r
806   InitCommonControls();\r
807 \r
808   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
809   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
810   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
811 \r
812   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
813 \r
814   while (GetMessage(&msg, /* message structure */\r
815                     NULL, /* handle of window receiving the message */\r
816                     0,    /* lowest message to examine */\r
817                     0))   /* highest message to examine */\r
818     {\r
819 \r
820       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
821         // [HGM] navigate: switch between all windows with tab\r
822         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
823         int i, currentElement = 0;\r
824 \r
825         // first determine what element of the chain we come from (if any)\r
826         if(appData.icsActive) {\r
827             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
828             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
829         }\r
830         if(engineOutputDialog && EngineOutputIsUp()) {\r
831             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
832             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
833         }\r
834         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
835             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
836         }\r
837         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
838         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
839         if(msg.hwnd == e1)                 currentElement = 2; else\r
840         if(msg.hwnd == e2)                 currentElement = 3; else\r
841         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
842         if(msg.hwnd == mh)                currentElement = 4; else\r
843         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
844         if(msg.hwnd == hText)  currentElement = 5; else\r
845         if(msg.hwnd == hInput) currentElement = 6; else\r
846         for (i = 0; i < N_BUTTONS; i++) {\r
847             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
848         }\r
849 \r
850         // determine where to go to\r
851         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
852           do {\r
853             currentElement = (currentElement + direction) % 7;\r
854             switch(currentElement) {\r
855                 case 0:\r
856                   h = hwndMain; break; // passing this case always makes the loop exit\r
857                 case 1:\r
858                   h = buttonDesc[0].hwnd; break; // could be NULL\r
859                 case 2:\r
860                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
861                   h = e1; break;\r
862                 case 3:\r
863                   if(!EngineOutputIsUp()) continue;\r
864                   h = e2; break;\r
865                 case 4:\r
866                   if(!MoveHistoryIsUp()) continue;\r
867                   h = mh; break;\r
868 //              case 6: // input to eval graph does not seem to get here!\r
869 //                if(!EvalGraphIsUp()) continue;\r
870 //                h = evalGraphDialog; break;\r
871                 case 5:\r
872                   if(!appData.icsActive) continue;\r
873                   SAY("display");\r
874                   h = hText; break;\r
875                 case 6:\r
876                   if(!appData.icsActive) continue;\r
877                   SAY("input");\r
878                   h = hInput; break;\r
879             }\r
880           } while(h == 0);\r
881 \r
882           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
883           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
884           SetFocus(h);\r
885 \r
886           continue; // this message now has been processed\r
887         }\r
888       }\r
889 \r
890       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
891           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
892           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
893           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
894           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
895           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
896           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
897           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
898           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
899           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
900         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
901         for(i=0; i<MAX_CHAT; i++) \r
902             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
903                 done = 1; break;\r
904         }\r
905         if(done) continue; // [HGM] chat: end patch\r
906         TranslateMessage(&msg); /* Translates virtual key codes */\r
907         DispatchMessage(&msg);  /* Dispatches message to window */\r
908       }\r
909     }\r
910 \r
911 \r
912   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
913 }\r
914 \r
915 /*---------------------------------------------------------------------------*\\r
916  *\r
917  * Initialization functions\r
918  *\r
919 \*---------------------------------------------------------------------------*/\r
920 \r
921 void\r
922 SetUserLogo()\r
923 {   // update user logo if necessary\r
924     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
925 \r
926     if(appData.autoLogo) {\r
927           curName = UserName();\r
928           if(strcmp(curName, oldUserName)) {\r
929                 GetCurrentDirectory(MSG_SIZ, dir);\r
930                 SetCurrentDirectory(installDir);\r
931                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
932                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
933                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
934                 if(userLogo == NULL)\r
935                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
936                 SetCurrentDirectory(dir); /* return to prev directory */\r
937           }\r
938     }\r
939 }\r
940 \r
941 BOOL\r
942 InitApplication(HINSTANCE hInstance)\r
943 {\r
944   WNDCLASS wc;\r
945 \r
946   /* Fill in window class structure with parameters that describe the */\r
947   /* main window. */\r
948 \r
949   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
950   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
951   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
952   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
953   wc.hInstance     = hInstance;         /* Owner of this class */\r
954   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
955   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
956   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
957   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
958   wc.lpszClassName = szAppName;                 /* Name to register as */\r
959 \r
960   /* Register the window class and return success/failure code. */\r
961   if (!RegisterClass(&wc)) return FALSE;\r
962 \r
963   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
964   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
965   wc.cbClsExtra    = 0;\r
966   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
967   wc.hInstance     = hInstance;\r
968   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
969   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
970   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
971   wc.lpszMenuName  = NULL;\r
972   wc.lpszClassName = szConsoleName;\r
973 \r
974   if (!RegisterClass(&wc)) return FALSE;\r
975   return TRUE;\r
976 }\r
977 \r
978 \r
979 /* Set by InitInstance, used by EnsureOnScreen */\r
980 int screenHeight, screenWidth;\r
981 \r
982 void\r
983 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
984 {\r
985 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
986   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
987   if (*x > screenWidth - 32) *x = 0;\r
988   if (*y > screenHeight - 32) *y = 0;\r
989   if (*x < minX) *x = minX;\r
990   if (*y < minY) *y = minY;\r
991 }\r
992 \r
993 VOID\r
994 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
995 {\r
996   char buf[MSG_SIZ], dir[MSG_SIZ];\r
997   GetCurrentDirectory(MSG_SIZ, dir);\r
998   SetCurrentDirectory(installDir);\r
999   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1000       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1001 \r
1002       if (cps->programLogo == NULL && appData.debugMode) {\r
1003           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1004       }\r
1005   } else if(appData.autoLogo) {\r
1006       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1007         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1008         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1009       } else\r
1010       if(appData.directory[n] && appData.directory[n][0]) {\r
1011         SetCurrentDirectory(appData.directory[n]);\r
1012         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1013       }\r
1014   }\r
1015   SetCurrentDirectory(dir); /* return to prev directory */\r
1016 }\r
1017 \r
1018 VOID\r
1019 InitTextures()\r
1020 {\r
1021   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1022   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1023   \r
1024   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1025       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1026       liteBackTextureMode = appData.liteBackTextureMode;\r
1027 \r
1028       if (liteBackTexture == NULL && appData.debugMode) {\r
1029           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1030       }\r
1031   }\r
1032   \r
1033   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1034       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035       darkBackTextureMode = appData.darkBackTextureMode;\r
1036 \r
1037       if (darkBackTexture == NULL && appData.debugMode) {\r
1038           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1039       }\r
1040   }\r
1041 }\r
1042 \r
1043 BOOL\r
1044 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1045 {\r
1046   HWND hwnd; /* Main window handle. */\r
1047   int ibs;\r
1048   WINDOWPLACEMENT wp;\r
1049   char *filepart;\r
1050 \r
1051   hInst = hInstance;    /* Store instance handle in our global variable */\r
1052   programName = szAppName;\r
1053 \r
1054   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1055     *filepart = NULLCHAR;\r
1056   } else {\r
1057     GetCurrentDirectory(MSG_SIZ, installDir);\r
1058   }\r
1059   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1060   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1061   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1062   /* xboard, and older WinBoards, controlled the move sound with the\r
1063      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1064      always turn the option on (so that the backend will call us),\r
1065      then let the user turn the sound off by setting it to silence if\r
1066      desired.  To accommodate old winboard.ini files saved by old\r
1067      versions of WinBoard, we also turn off the sound if the option\r
1068      was initially set to false. [HGM] taken out of InitAppData */\r
1069   if (!appData.ringBellAfterMoves) {\r
1070     sounds[(int)SoundMove].name = strdup("");\r
1071     appData.ringBellAfterMoves = TRUE;\r
1072   }\r
1073   if (appData.debugMode) {\r
1074     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1075     setbuf(debugFP, NULL);\r
1076   }\r
1077 \r
1078   LoadLanguageFile(appData.language);\r
1079 \r
1080   InitBackEnd1();\r
1081 \r
1082 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1083 //  InitEngineUCI( installDir, &second );\r
1084 \r
1085   /* Create a main window for this application instance. */\r
1086   hwnd = CreateWindow(szAppName, szTitle,\r
1087                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1088                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1089                       NULL, NULL, hInstance, NULL);\r
1090   hwndMain = hwnd;\r
1091 \r
1092   /* If window could not be created, return "failure" */\r
1093   if (!hwnd) {\r
1094     return (FALSE);\r
1095   }\r
1096 \r
1097   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1098   LoadLogo(&first, 0, FALSE);\r
1099   LoadLogo(&second, 1, appData.icsActive);\r
1100 \r
1101   SetUserLogo();\r
1102 \r
1103   iconWhite = LoadIcon(hInstance, "icon_white");\r
1104   iconBlack = LoadIcon(hInstance, "icon_black");\r
1105   iconCurrent = iconWhite;\r
1106   InitDrawingColors();\r
1107   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1108   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1109   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1110     /* Compute window size for each board size, and use the largest\r
1111        size that fits on this screen as the default. */\r
1112     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1113     if (boardSize == (BoardSize)-1 &&\r
1114         winH <= screenHeight\r
1115            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1116         && winW <= screenWidth) {\r
1117       boardSize = (BoardSize)ibs;\r
1118     }\r
1119   }\r
1120 \r
1121   InitDrawingSizes(boardSize, 0);\r
1122   RecentEngineMenu(appData.recentEngineList);\r
1123   InitMenuChecks();\r
1124   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1125 \r
1126   /* [AS] Load textures if specified */\r
1127   InitTextures();\r
1128 \r
1129   mysrandom( (unsigned) time(NULL) );\r
1130 \r
1131   /* [AS] Restore layout */\r
1132   if( wpMoveHistory.visible ) {\r
1133       MoveHistoryPopUp();\r
1134   }\r
1135 \r
1136   if( wpEvalGraph.visible ) {\r
1137       EvalGraphPopUp();\r
1138   }\r
1139 \r
1140   if( wpEngineOutput.visible ) {\r
1141       EngineOutputPopUp();\r
1142   }\r
1143 \r
1144   /* Make the window visible; update its client area; and return "success" */\r
1145   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1146   wp.length = sizeof(WINDOWPLACEMENT);\r
1147   wp.flags = 0;\r
1148   wp.showCmd = nCmdShow;\r
1149   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1150   wp.rcNormalPosition.left = wpMain.x;\r
1151   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1152   wp.rcNormalPosition.top = wpMain.y;\r
1153   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1154   SetWindowPlacement(hwndMain, &wp);\r
1155 \r
1156   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1157 \r
1158   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1159                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1160 \r
1161   if (hwndConsole) {\r
1162 #if AOT_CONSOLE\r
1163     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1164                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1165 #endif\r
1166     ShowWindow(hwndConsole, nCmdShow);\r
1167     SetActiveWindow(hwndConsole);\r
1168   }\r
1169   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1170   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1171 \r
1172   return TRUE;\r
1173 \r
1174 }\r
1175 \r
1176 VOID\r
1177 InitMenuChecks()\r
1178 {\r
1179   HMENU hmenu = GetMenu(hwndMain);\r
1180 \r
1181   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1182                         MF_BYCOMMAND|((appData.icsActive &&\r
1183                                        *appData.icsCommPort != NULLCHAR) ?\r
1184                                       MF_ENABLED : MF_GRAYED));\r
1185   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1186                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1187                                      MF_CHECKED : MF_UNCHECKED));\r
1188 }\r
1189 \r
1190 //---------------------------------------------------------------------------------------------------------\r
1191 \r
1192 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1193 #define XBOARD FALSE\r
1194 \r
1195 #define OPTCHAR "/"\r
1196 #define SEPCHAR "="\r
1197 \r
1198 #include "args.h"\r
1199 \r
1200 // front-end part of option handling\r
1201 \r
1202 VOID\r
1203 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1204 {\r
1205   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1206   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1207   DeleteDC(hdc);\r
1208   lf->lfWidth = 0;\r
1209   lf->lfEscapement = 0;\r
1210   lf->lfOrientation = 0;\r
1211   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1212   lf->lfItalic = mfp->italic;\r
1213   lf->lfUnderline = mfp->underline;\r
1214   lf->lfStrikeOut = mfp->strikeout;\r
1215   lf->lfCharSet = mfp->charset;\r
1216   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1217   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1218   lf->lfQuality = DEFAULT_QUALITY;\r
1219   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1220     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1221 }\r
1222 \r
1223 void\r
1224 CreateFontInMF(MyFont *mf)\r
1225\r
1226   LFfromMFP(&mf->lf, &mf->mfp);\r
1227   if (mf->hf) DeleteObject(mf->hf);\r
1228   mf->hf = CreateFontIndirect(&mf->lf);\r
1229 }\r
1230 \r
1231 // [HGM] This platform-dependent table provides the location for storing the color info\r
1232 void *\r
1233 colorVariable[] = {\r
1234   &whitePieceColor, \r
1235   &blackPieceColor, \r
1236   &lightSquareColor,\r
1237   &darkSquareColor, \r
1238   &highlightSquareColor,\r
1239   &premoveHighlightColor,\r
1240   NULL,\r
1241   &consoleBackgroundColor,\r
1242   &appData.fontForeColorWhite,\r
1243   &appData.fontBackColorWhite,\r
1244   &appData.fontForeColorBlack,\r
1245   &appData.fontBackColorBlack,\r
1246   &appData.evalHistColorWhite,\r
1247   &appData.evalHistColorBlack,\r
1248   &appData.highlightArrowColor,\r
1249 };\r
1250 \r
1251 /* Command line font name parser.  NULL name means do nothing.\r
1252    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1253    For backward compatibility, syntax without the colon is also\r
1254    accepted, but font names with digits in them won't work in that case.\r
1255 */\r
1256 VOID\r
1257 ParseFontName(char *name, MyFontParams *mfp)\r
1258 {\r
1259   char *p, *q;\r
1260   if (name == NULL) return;\r
1261   p = name;\r
1262   q = strchr(p, ':');\r
1263   if (q) {\r
1264     if (q - p >= sizeof(mfp->faceName))\r
1265       ExitArgError(_("Font name too long:"), name, TRUE);\r
1266     memcpy(mfp->faceName, p, q - p);\r
1267     mfp->faceName[q - p] = NULLCHAR;\r
1268     p = q + 1;\r
1269   } else {\r
1270     q = mfp->faceName;\r
1271     while (*p && !isdigit(*p)) {\r
1272       *q++ = *p++;\r
1273       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1274         ExitArgError(_("Font name too long:"), name, TRUE);\r
1275     }\r
1276     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1277     *q = NULLCHAR;\r
1278   }\r
1279   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1280   mfp->pointSize = (float) atof(p);\r
1281   mfp->bold = (strchr(p, 'b') != NULL);\r
1282   mfp->italic = (strchr(p, 'i') != NULL);\r
1283   mfp->underline = (strchr(p, 'u') != NULL);\r
1284   mfp->strikeout = (strchr(p, 's') != NULL);\r
1285   mfp->charset = DEFAULT_CHARSET;\r
1286   q = strchr(p, 'c');\r
1287   if (q)\r
1288     mfp->charset = (BYTE) atoi(q+1);\r
1289 }\r
1290 \r
1291 void\r
1292 ParseFont(char *name, int number)\r
1293 { // wrapper to shield back-end from 'font'\r
1294   ParseFontName(name, &font[boardSize][number]->mfp);\r
1295 }\r
1296 \r
1297 void\r
1298 SetFontDefaults()\r
1299 { // in WB  we have a 2D array of fonts; this initializes their description\r
1300   int i, j;\r
1301   /* Point font array elements to structures and\r
1302      parse default font names */\r
1303   for (i=0; i<NUM_FONTS; i++) {\r
1304     for (j=0; j<NUM_SIZES; j++) {\r
1305       font[j][i] = &fontRec[j][i];\r
1306       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1307     }\r
1308   }\r
1309 }\r
1310 \r
1311 void\r
1312 CreateFonts()\r
1313 { // here we create the actual fonts from the selected descriptions\r
1314   int i, j;\r
1315   for (i=0; i<NUM_FONTS; i++) {\r
1316     for (j=0; j<NUM_SIZES; j++) {\r
1317       CreateFontInMF(font[j][i]);\r
1318     }\r
1319   }\r
1320 }\r
1321 /* Color name parser.\r
1322    X version accepts X color names, but this one\r
1323    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1324 COLORREF\r
1325 ParseColorName(char *name)\r
1326 {\r
1327   int red, green, blue, count;\r
1328   char buf[MSG_SIZ];\r
1329 \r
1330   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1331   if (count != 3) {\r
1332     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1333       &red, &green, &blue);\r
1334   }\r
1335   if (count != 3) {\r
1336     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1337     DisplayError(buf, 0);\r
1338     return RGB(0, 0, 0);\r
1339   }\r
1340   return PALETTERGB(red, green, blue);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseColor(int n, char *name)\r
1345 { // for WinBoard the color is an int, which needs to be derived from the string\r
1346   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1347 }\r
1348 \r
1349 void\r
1350 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1351 {\r
1352   char *e = argValue;\r
1353   int eff = 0;\r
1354 \r
1355   while (*e) {\r
1356     if (*e == 'b')      eff |= CFE_BOLD;\r
1357     else if (*e == 'i') eff |= CFE_ITALIC;\r
1358     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1359     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1360     else if (*e == '#' || isdigit(*e)) break;\r
1361     e++;\r
1362   }\r
1363   *effects = eff;\r
1364   *color   = ParseColorName(e);\r
1365 }\r
1366 \r
1367 void\r
1368 ParseTextAttribs(ColorClass cc, char *s)\r
1369 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1370     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1371     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1372 }\r
1373 \r
1374 void\r
1375 ParseBoardSize(void *addr, char *name)\r
1376 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1377   BoardSize bs = SizeTiny;\r
1378   while (sizeInfo[bs].name != NULL) {\r
1379     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1380         *(BoardSize *)addr = bs;\r
1381         return;\r
1382     }\r
1383     bs++;\r
1384   }\r
1385   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1386 }\r
1387 \r
1388 void\r
1389 LoadAllSounds()\r
1390 { // [HGM] import name from appData first\r
1391   ColorClass cc;\r
1392   SoundClass sc;\r
1393   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1394     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1395     textAttribs[cc].sound.data = NULL;\r
1396     MyLoadSound(&textAttribs[cc].sound);\r
1397   }\r
1398   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1399     textAttribs[cc].sound.name = strdup("");\r
1400     textAttribs[cc].sound.data = NULL;\r
1401   }\r
1402   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1403     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1404     sounds[sc].data = NULL;\r
1405     MyLoadSound(&sounds[sc]);\r
1406   }\r
1407 }\r
1408 \r
1409 void\r
1410 SetCommPortDefaults()\r
1411 {\r
1412    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1413   dcb.DCBlength = sizeof(DCB);\r
1414   dcb.BaudRate = 9600;\r
1415   dcb.fBinary = TRUE;\r
1416   dcb.fParity = FALSE;\r
1417   dcb.fOutxCtsFlow = FALSE;\r
1418   dcb.fOutxDsrFlow = FALSE;\r
1419   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1420   dcb.fDsrSensitivity = FALSE;\r
1421   dcb.fTXContinueOnXoff = TRUE;\r
1422   dcb.fOutX = FALSE;\r
1423   dcb.fInX = FALSE;\r
1424   dcb.fNull = FALSE;\r
1425   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1426   dcb.fAbortOnError = FALSE;\r
1427   dcb.ByteSize = 7;\r
1428   dcb.Parity = SPACEPARITY;\r
1429   dcb.StopBits = ONESTOPBIT;\r
1430 }\r
1431 \r
1432 // [HGM] args: these three cases taken out to stay in front-end\r
1433 void\r
1434 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1436         // while the curent board size determines the element. This system should be ported to XBoard.\r
1437         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1438         int bs;\r
1439         for (bs=0; bs<NUM_SIZES; bs++) {\r
1440           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1441           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1442           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1443             ad->argName, mfp->faceName, mfp->pointSize,\r
1444             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1445             mfp->bold ? "b" : "",\r
1446             mfp->italic ? "i" : "",\r
1447             mfp->underline ? "u" : "",\r
1448             mfp->strikeout ? "s" : "",\r
1449             (int)mfp->charset);\r
1450         }\r
1451       }\r
1452 \r
1453 void\r
1454 ExportSounds()\r
1455 { // [HGM] copy the names from the internal WB variables to appData\r
1456   ColorClass cc;\r
1457   SoundClass sc;\r
1458   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1459     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1460   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1461     (&appData.soundMove)[sc] = sounds[sc].name;\r
1462 }\r
1463 \r
1464 void\r
1465 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1466 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1467         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1468         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1469           (ta->effects & CFE_BOLD) ? "b" : "",\r
1470           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1471           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1472           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1473           (ta->effects) ? " " : "",\r
1474           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1475       }\r
1476 \r
1477 void\r
1478 SaveColor(FILE *f, ArgDescriptor *ad)\r
1479 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1480         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1481         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1482           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1483 }\r
1484 \r
1485 void\r
1486 SaveBoardSize(FILE *f, char *name, void *addr)\r
1487 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1488   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1489 }\r
1490 \r
1491 void\r
1492 ParseCommPortSettings(char *s)\r
1493 { // wrapper to keep dcb from back-end\r
1494   ParseCommSettings(s, &dcb);\r
1495 }\r
1496 \r
1497 void\r
1498 GetWindowCoords()\r
1499 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1500   GetActualPlacement(hwndMain, &wpMain);\r
1501   GetActualPlacement(hwndConsole, &wpConsole);\r
1502   GetActualPlacement(commentDialog, &wpComment);\r
1503   GetActualPlacement(editTagsDialog, &wpTags);\r
1504   GetActualPlacement(gameListDialog, &wpGameList);\r
1505   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1506   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1507   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1508 }\r
1509 \r
1510 void\r
1511 PrintCommPortSettings(FILE *f, char *name)\r
1512 { // wrapper to shield back-end from DCB\r
1513       PrintCommSettings(f, name, &dcb);\r
1514 }\r
1515 \r
1516 int\r
1517 MySearchPath(char *installDir, char *name, char *fullname)\r
1518 {\r
1519   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1520   if(name[0]== '%') {\r
1521     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1522     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1523       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1524       *strchr(buf, '%') = 0;\r
1525       strcat(fullname, getenv(buf));\r
1526       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1527     }\r
1528     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1529     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1530     return (int) strlen(fullname);\r
1531   }\r
1532   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1533 }\r
1534 \r
1535 int\r
1536 MyGetFullPathName(char *name, char *fullname)\r
1537 {\r
1538   char *dummy;\r
1539   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1540 }\r
1541 \r
1542 int\r
1543 MainWindowUp()\r
1544 { // [HGM] args: allows testing if main window is realized from back-end\r
1545   return hwndMain != NULL;\r
1546 }\r
1547 \r
1548 void\r
1549 PopUpStartupDialog()\r
1550 {\r
1551     FARPROC lpProc;\r
1552     \r
1553     LoadLanguageFile(appData.language);\r
1554     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1555     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1556     FreeProcInstance(lpProc);\r
1557 }\r
1558 \r
1559 /*---------------------------------------------------------------------------*\\r
1560  *\r
1561  * GDI board drawing routines\r
1562  *\r
1563 \*---------------------------------------------------------------------------*/\r
1564 \r
1565 /* [AS] Draw square using background texture */\r
1566 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1567 {\r
1568     XFORM   x;\r
1569 \r
1570     if( mode == 0 ) {\r
1571         return; /* Should never happen! */\r
1572     }\r
1573 \r
1574     SetGraphicsMode( dst, GM_ADVANCED );\r
1575 \r
1576     switch( mode ) {\r
1577     case 1:\r
1578         /* Identity */\r
1579         break;\r
1580     case 2:\r
1581         /* X reflection */\r
1582         x.eM11 = -1.0;\r
1583         x.eM12 = 0;\r
1584         x.eM21 = 0;\r
1585         x.eM22 = 1.0;\r
1586         x.eDx = (FLOAT) dw + dx - 1;\r
1587         x.eDy = 0;\r
1588         dx = 0;\r
1589         SetWorldTransform( dst, &x );\r
1590         break;\r
1591     case 3:\r
1592         /* Y reflection */\r
1593         x.eM11 = 1.0;\r
1594         x.eM12 = 0;\r
1595         x.eM21 = 0;\r
1596         x.eM22 = -1.0;\r
1597         x.eDx = 0;\r
1598         x.eDy = (FLOAT) dh + dy - 1;\r
1599         dy = 0;\r
1600         SetWorldTransform( dst, &x );\r
1601         break;\r
1602     case 4:\r
1603         /* X/Y flip */\r
1604         x.eM11 = 0;\r
1605         x.eM12 = 1.0;\r
1606         x.eM21 = 1.0;\r
1607         x.eM22 = 0;\r
1608         x.eDx = (FLOAT) dx;\r
1609         x.eDy = (FLOAT) dy;\r
1610         dx = 0;\r
1611         dy = 0;\r
1612         SetWorldTransform( dst, &x );\r
1613         break;\r
1614     }\r
1615 \r
1616     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1617 \r
1618     x.eM11 = 1.0;\r
1619     x.eM12 = 0;\r
1620     x.eM21 = 0;\r
1621     x.eM22 = 1.0;\r
1622     x.eDx = 0;\r
1623     x.eDy = 0;\r
1624     SetWorldTransform( dst, &x );\r
1625 \r
1626     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1627 }\r
1628 \r
1629 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1630 enum {\r
1631     PM_WP = (int) WhitePawn, \r
1632     PM_WN = (int) WhiteKnight, \r
1633     PM_WB = (int) WhiteBishop, \r
1634     PM_WR = (int) WhiteRook, \r
1635     PM_WQ = (int) WhiteQueen, \r
1636     PM_WF = (int) WhiteFerz, \r
1637     PM_WW = (int) WhiteWazir, \r
1638     PM_WE = (int) WhiteAlfil, \r
1639     PM_WM = (int) WhiteMan, \r
1640     PM_WO = (int) WhiteCannon, \r
1641     PM_WU = (int) WhiteUnicorn, \r
1642     PM_WH = (int) WhiteNightrider, \r
1643     PM_WA = (int) WhiteAngel, \r
1644     PM_WC = (int) WhiteMarshall, \r
1645     PM_WAB = (int) WhiteCardinal, \r
1646     PM_WD = (int) WhiteDragon, \r
1647     PM_WL = (int) WhiteLance, \r
1648     PM_WS = (int) WhiteCobra, \r
1649     PM_WV = (int) WhiteFalcon, \r
1650     PM_WSG = (int) WhiteSilver, \r
1651     PM_WG = (int) WhiteGrasshopper, \r
1652     PM_WK = (int) WhiteKing,\r
1653     PM_BP = (int) BlackPawn, \r
1654     PM_BN = (int) BlackKnight, \r
1655     PM_BB = (int) BlackBishop, \r
1656     PM_BR = (int) BlackRook, \r
1657     PM_BQ = (int) BlackQueen, \r
1658     PM_BF = (int) BlackFerz, \r
1659     PM_BW = (int) BlackWazir, \r
1660     PM_BE = (int) BlackAlfil, \r
1661     PM_BM = (int) BlackMan,\r
1662     PM_BO = (int) BlackCannon, \r
1663     PM_BU = (int) BlackUnicorn, \r
1664     PM_BH = (int) BlackNightrider, \r
1665     PM_BA = (int) BlackAngel, \r
1666     PM_BC = (int) BlackMarshall, \r
1667     PM_BG = (int) BlackGrasshopper, \r
1668     PM_BAB = (int) BlackCardinal,\r
1669     PM_BD = (int) BlackDragon,\r
1670     PM_BL = (int) BlackLance,\r
1671     PM_BS = (int) BlackCobra,\r
1672     PM_BV = (int) BlackFalcon,\r
1673     PM_BSG = (int) BlackSilver,\r
1674     PM_BK = (int) BlackKing\r
1675 };\r
1676 \r
1677 static HFONT hPieceFont = NULL;\r
1678 static HBITMAP hPieceMask[(int) EmptySquare];\r
1679 static HBITMAP hPieceFace[(int) EmptySquare];\r
1680 static int fontBitmapSquareSize = 0;\r
1681 static char pieceToFontChar[(int) EmptySquare] =\r
1682                               { 'p', 'n', 'b', 'r', 'q', \r
1683                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1684                       'k', 'o', 'm', 'v', 't', 'w', \r
1685                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1686                                                               'l' };\r
1687 \r
1688 extern BOOL SetCharTable( char *table, const char * map );\r
1689 /* [HGM] moved to backend.c */\r
1690 \r
1691 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1692 {\r
1693     HBRUSH hbrush;\r
1694     BYTE r1 = GetRValue( color );\r
1695     BYTE g1 = GetGValue( color );\r
1696     BYTE b1 = GetBValue( color );\r
1697     BYTE r2 = r1 / 2;\r
1698     BYTE g2 = g1 / 2;\r
1699     BYTE b2 = b1 / 2;\r
1700     RECT rc;\r
1701 \r
1702     /* Create a uniform background first */\r
1703     hbrush = CreateSolidBrush( color );\r
1704     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1705     FillRect( hdc, &rc, hbrush );\r
1706     DeleteObject( hbrush );\r
1707     \r
1708     if( mode == 1 ) {\r
1709         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1710         int steps = squareSize / 2;\r
1711         int i;\r
1712 \r
1713         for( i=0; i<steps; i++ ) {\r
1714             BYTE r = r1 - (r1-r2) * i / steps;\r
1715             BYTE g = g1 - (g1-g2) * i / steps;\r
1716             BYTE b = b1 - (b1-b2) * i / steps;\r
1717 \r
1718             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1719             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1720             FillRect( hdc, &rc, hbrush );\r
1721             DeleteObject(hbrush);\r
1722         }\r
1723     }\r
1724     else if( mode == 2 ) {\r
1725         /* Diagonal gradient, good more or less for every piece */\r
1726         POINT triangle[3];\r
1727         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1728         HBRUSH hbrush_old;\r
1729         int steps = squareSize;\r
1730         int i;\r
1731 \r
1732         triangle[0].x = squareSize - steps;\r
1733         triangle[0].y = squareSize;\r
1734         triangle[1].x = squareSize;\r
1735         triangle[1].y = squareSize;\r
1736         triangle[2].x = squareSize;\r
1737         triangle[2].y = squareSize - steps;\r
1738 \r
1739         for( i=0; i<steps; i++ ) {\r
1740             BYTE r = r1 - (r1-r2) * i / steps;\r
1741             BYTE g = g1 - (g1-g2) * i / steps;\r
1742             BYTE b = b1 - (b1-b2) * i / steps;\r
1743 \r
1744             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1745             hbrush_old = SelectObject( hdc, hbrush );\r
1746             Polygon( hdc, triangle, 3 );\r
1747             SelectObject( hdc, hbrush_old );\r
1748             DeleteObject(hbrush);\r
1749             triangle[0].x++;\r
1750             triangle[2].y++;\r
1751         }\r
1752 \r
1753         SelectObject( hdc, hpen );\r
1754     }\r
1755 }\r
1756 \r
1757 /*\r
1758     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1759     seems to work ok. The main problem here is to find the "inside" of a chess\r
1760     piece: follow the steps as explained below.\r
1761 */\r
1762 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1763 {\r
1764     HBITMAP hbm;\r
1765     HBITMAP hbm_old;\r
1766     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1767     RECT rc;\r
1768     SIZE sz;\r
1769     POINT pt;\r
1770     int backColor = whitePieceColor; \r
1771     int foreColor = blackPieceColor;\r
1772     \r
1773     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1774         backColor = appData.fontBackColorWhite;\r
1775         foreColor = appData.fontForeColorWhite;\r
1776     }\r
1777     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1778         backColor = appData.fontBackColorBlack;\r
1779         foreColor = appData.fontForeColorBlack;\r
1780     }\r
1781 \r
1782     /* Mask */\r
1783     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1784 \r
1785     hbm_old = SelectObject( hdc, hbm );\r
1786 \r
1787     rc.left = 0;\r
1788     rc.top = 0;\r
1789     rc.right = squareSize;\r
1790     rc.bottom = squareSize;\r
1791 \r
1792     /* Step 1: background is now black */\r
1793     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1794 \r
1795     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1796 \r
1797     pt.x = (squareSize - sz.cx) / 2;\r
1798     pt.y = (squareSize - sz.cy) / 2;\r
1799 \r
1800     SetBkMode( hdc, TRANSPARENT );\r
1801     SetTextColor( hdc, chroma );\r
1802     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1803     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1804 \r
1805     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1806     /* Step 3: the area outside the piece is filled with white */\r
1807 //    FloodFill( hdc, 0, 0, chroma );\r
1808     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1809     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1810     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1811     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1812     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1813     /* \r
1814         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1815         but if the start point is not inside the piece we're lost!\r
1816         There should be a better way to do this... if we could create a region or path\r
1817         from the fill operation we would be fine for example.\r
1818     */\r
1819 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1820     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1821 \r
1822     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1823         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1824         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1825 \r
1826         SelectObject( dc2, bm2 );\r
1827         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1828         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1829         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1830         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1831         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteObject( bm2 );\r
1835     }\r
1836 \r
1837     SetTextColor( hdc, 0 );\r
1838     /* \r
1839         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1840         draw the piece again in black for safety.\r
1841     */\r
1842     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1843 \r
1844     SelectObject( hdc, hbm_old );\r
1845 \r
1846     if( hPieceMask[index] != NULL ) {\r
1847         DeleteObject( hPieceMask[index] );\r
1848     }\r
1849 \r
1850     hPieceMask[index] = hbm;\r
1851 \r
1852     /* Face */\r
1853     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1854 \r
1855     SelectObject( hdc, hbm );\r
1856 \r
1857     {\r
1858         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1859         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1860         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1861 \r
1862         SelectObject( dc1, hPieceMask[index] );\r
1863         SelectObject( dc2, bm2 );\r
1864         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1865         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1866         \r
1867         /* \r
1868             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1869             the piece background and deletes (makes transparent) the rest.\r
1870             Thanks to that mask, we are free to paint the background with the greates\r
1871             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1872             We use this, to make gradients and give the pieces a "roundish" look.\r
1873         */\r
1874         SetPieceBackground( hdc, backColor, 2 );\r
1875         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1876 \r
1877         DeleteDC( dc2 );\r
1878         DeleteDC( dc1 );\r
1879         DeleteObject( bm2 );\r
1880     }\r
1881 \r
1882     SetTextColor( hdc, foreColor );\r
1883     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1884 \r
1885     SelectObject( hdc, hbm_old );\r
1886 \r
1887     if( hPieceFace[index] != NULL ) {\r
1888         DeleteObject( hPieceFace[index] );\r
1889     }\r
1890 \r
1891     hPieceFace[index] = hbm;\r
1892 }\r
1893 \r
1894 static int TranslatePieceToFontPiece( int piece )\r
1895 {\r
1896     switch( piece ) {\r
1897     case BlackPawn:\r
1898         return PM_BP;\r
1899     case BlackKnight:\r
1900         return PM_BN;\r
1901     case BlackBishop:\r
1902         return PM_BB;\r
1903     case BlackRook:\r
1904         return PM_BR;\r
1905     case BlackQueen:\r
1906         return PM_BQ;\r
1907     case BlackKing:\r
1908         return PM_BK;\r
1909     case WhitePawn:\r
1910         return PM_WP;\r
1911     case WhiteKnight:\r
1912         return PM_WN;\r
1913     case WhiteBishop:\r
1914         return PM_WB;\r
1915     case WhiteRook:\r
1916         return PM_WR;\r
1917     case WhiteQueen:\r
1918         return PM_WQ;\r
1919     case WhiteKing:\r
1920         return PM_WK;\r
1921 \r
1922     case BlackAngel:\r
1923         return PM_BA;\r
1924     case BlackMarshall:\r
1925         return PM_BC;\r
1926     case BlackFerz:\r
1927         return PM_BF;\r
1928     case BlackNightrider:\r
1929         return PM_BH;\r
1930     case BlackAlfil:\r
1931         return PM_BE;\r
1932     case BlackWazir:\r
1933         return PM_BW;\r
1934     case BlackUnicorn:\r
1935         return PM_BU;\r
1936     case BlackCannon:\r
1937         return PM_BO;\r
1938     case BlackGrasshopper:\r
1939         return PM_BG;\r
1940     case BlackMan:\r
1941         return PM_BM;\r
1942     case BlackSilver:\r
1943         return PM_BSG;\r
1944     case BlackLance:\r
1945         return PM_BL;\r
1946     case BlackFalcon:\r
1947         return PM_BV;\r
1948     case BlackCobra:\r
1949         return PM_BS;\r
1950     case BlackCardinal:\r
1951         return PM_BAB;\r
1952     case BlackDragon:\r
1953         return PM_BD;\r
1954 \r
1955     case WhiteAngel:\r
1956         return PM_WA;\r
1957     case WhiteMarshall:\r
1958         return PM_WC;\r
1959     case WhiteFerz:\r
1960         return PM_WF;\r
1961     case WhiteNightrider:\r
1962         return PM_WH;\r
1963     case WhiteAlfil:\r
1964         return PM_WE;\r
1965     case WhiteWazir:\r
1966         return PM_WW;\r
1967     case WhiteUnicorn:\r
1968         return PM_WU;\r
1969     case WhiteCannon:\r
1970         return PM_WO;\r
1971     case WhiteGrasshopper:\r
1972         return PM_WG;\r
1973     case WhiteMan:\r
1974         return PM_WM;\r
1975     case WhiteSilver:\r
1976         return PM_WSG;\r
1977     case WhiteLance:\r
1978         return PM_WL;\r
1979     case WhiteFalcon:\r
1980         return PM_WV;\r
1981     case WhiteCobra:\r
1982         return PM_WS;\r
1983     case WhiteCardinal:\r
1984         return PM_WAB;\r
1985     case WhiteDragon:\r
1986         return PM_WD;\r
1987     }\r
1988 \r
1989     return 0;\r
1990 }\r
1991 \r
1992 void CreatePiecesFromFont()\r
1993 {\r
1994     LOGFONT lf;\r
1995     HDC hdc_window = NULL;\r
1996     HDC hdc = NULL;\r
1997     HFONT hfont_old;\r
1998     int fontHeight;\r
1999     int i;\r
2000 \r
2001     if( fontBitmapSquareSize < 0 ) {\r
2002         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2003         return;\r
2004     }\r
2005 \r
2006     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2007             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2008         fontBitmapSquareSize = -1;\r
2009         return;\r
2010     }\r
2011 \r
2012     if( fontBitmapSquareSize != squareSize ) {\r
2013         hdc_window = GetDC( hwndMain );\r
2014         hdc = CreateCompatibleDC( hdc_window );\r
2015 \r
2016         if( hPieceFont != NULL ) {\r
2017             DeleteObject( hPieceFont );\r
2018         }\r
2019         else {\r
2020             for( i=0; i<=(int)BlackKing; i++ ) {\r
2021                 hPieceMask[i] = NULL;\r
2022                 hPieceFace[i] = NULL;\r
2023             }\r
2024         }\r
2025 \r
2026         fontHeight = 75;\r
2027 \r
2028         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2029             fontHeight = appData.fontPieceSize;\r
2030         }\r
2031 \r
2032         fontHeight = (fontHeight * squareSize) / 100;\r
2033 \r
2034         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2035         lf.lfWidth = 0;\r
2036         lf.lfEscapement = 0;\r
2037         lf.lfOrientation = 0;\r
2038         lf.lfWeight = FW_NORMAL;\r
2039         lf.lfItalic = 0;\r
2040         lf.lfUnderline = 0;\r
2041         lf.lfStrikeOut = 0;\r
2042         lf.lfCharSet = DEFAULT_CHARSET;\r
2043         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2044         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2045         lf.lfQuality = PROOF_QUALITY;\r
2046         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2047         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2048         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2049 \r
2050         hPieceFont = CreateFontIndirect( &lf );\r
2051 \r
2052         if( hPieceFont == NULL ) {\r
2053             fontBitmapSquareSize = -2;\r
2054         }\r
2055         else {\r
2056             /* Setup font-to-piece character table */\r
2057             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2058                 /* No (or wrong) global settings, try to detect the font */\r
2059                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2060                     /* Alpha */\r
2061                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2062                 }\r
2063                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2064                     /* DiagramTT* family */\r
2065                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2066                 }\r
2067                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2068                     /* Fairy symbols */\r
2069                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2070                 }\r
2071                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2072                     /* Good Companion (Some characters get warped as literal :-( */\r
2073                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2074                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2075                     SetCharTable(pieceToFontChar, s);\r
2076                 }\r
2077                 else {\r
2078                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2079                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2080                 }\r
2081             }\r
2082 \r
2083             /* Create bitmaps */\r
2084             hfont_old = SelectObject( hdc, hPieceFont );\r
2085             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2086                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2087                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2088 \r
2089             SelectObject( hdc, hfont_old );\r
2090 \r
2091             fontBitmapSquareSize = squareSize;\r
2092         }\r
2093     }\r
2094 \r
2095     if( hdc != NULL ) {\r
2096         DeleteDC( hdc );\r
2097     }\r
2098 \r
2099     if( hdc_window != NULL ) {\r
2100         ReleaseDC( hwndMain, hdc_window );\r
2101     }\r
2102 }\r
2103 \r
2104 HBITMAP\r
2105 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2106 {\r
2107   char name[128];\r
2108 \r
2109     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2110   if (gameInfo.event &&\r
2111       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2112       strcmp(name, "k80s") == 0) {\r
2113     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2114   }\r
2115   return LoadBitmap(hinst, name);\r
2116 }\r
2117 \r
2118 \r
2119 /* Insert a color into the program's logical palette\r
2120    structure.  This code assumes the given color is\r
2121    the result of the RGB or PALETTERGB macro, and it\r
2122    knows how those macros work (which is documented).\r
2123 */\r
2124 VOID\r
2125 InsertInPalette(COLORREF color)\r
2126 {\r
2127   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2128 \r
2129   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2130     DisplayFatalError(_("Too many colors"), 0, 1);\r
2131     pLogPal->palNumEntries--;\r
2132     return;\r
2133   }\r
2134 \r
2135   pe->peFlags = (char) 0;\r
2136   pe->peRed = (char) (0xFF & color);\r
2137   pe->peGreen = (char) (0xFF & (color >> 8));\r
2138   pe->peBlue = (char) (0xFF & (color >> 16));\r
2139   return;\r
2140 }\r
2141 \r
2142 \r
2143 VOID\r
2144 InitDrawingColors()\r
2145 {\r
2146   if (pLogPal == NULL) {\r
2147     /* Allocate enough memory for a logical palette with\r
2148      * PALETTESIZE entries and set the size and version fields\r
2149      * of the logical palette structure.\r
2150      */\r
2151     pLogPal = (NPLOGPALETTE)\r
2152       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2153                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2154     pLogPal->palVersion    = 0x300;\r
2155   }\r
2156   pLogPal->palNumEntries = 0;\r
2157 \r
2158   InsertInPalette(lightSquareColor);\r
2159   InsertInPalette(darkSquareColor);\r
2160   InsertInPalette(whitePieceColor);\r
2161   InsertInPalette(blackPieceColor);\r
2162   InsertInPalette(highlightSquareColor);\r
2163   InsertInPalette(premoveHighlightColor);\r
2164 \r
2165   /*  create a logical color palette according the information\r
2166    *  in the LOGPALETTE structure.\r
2167    */\r
2168   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2169 \r
2170   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2171   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2172   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2173   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2174   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2175   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2176   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2177   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2178   /* [AS] Force rendering of the font-based pieces */\r
2179   if( fontBitmapSquareSize > 0 ) {\r
2180     fontBitmapSquareSize = 0;\r
2181   }\r
2182 }\r
2183 \r
2184 \r
2185 int\r
2186 BoardWidth(int boardSize, int n)\r
2187 { /* [HGM] argument n added to allow different width and height */\r
2188   int lineGap = sizeInfo[boardSize].lineGap;\r
2189 \r
2190   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2191       lineGap = appData.overrideLineGap;\r
2192   }\r
2193 \r
2194   return (n + 1) * lineGap +\r
2195           n * sizeInfo[boardSize].squareSize;\r
2196 }\r
2197 \r
2198 /* Respond to board resize by dragging edge */\r
2199 VOID\r
2200 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2201 {\r
2202   BoardSize newSize = NUM_SIZES - 1;\r
2203   static int recurse = 0;\r
2204   if (IsIconic(hwndMain)) return;\r
2205   if (recurse > 0) return;\r
2206   recurse++;\r
2207   while (newSize > 0) {\r
2208         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2209         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2210            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2211     newSize--;\r
2212   } \r
2213   boardSize = newSize;\r
2214   InitDrawingSizes(boardSize, flags);\r
2215   recurse--;\r
2216 }\r
2217 \r
2218 \r
2219 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2220 \r
2221 VOID\r
2222 InitDrawingSizes(BoardSize boardSize, int flags)\r
2223 {\r
2224   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2225   ChessSquare piece;\r
2226   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2227   HDC hdc;\r
2228   SIZE clockSize, messageSize;\r
2229   HFONT oldFont;\r
2230   char buf[MSG_SIZ];\r
2231   char *str;\r
2232   HMENU hmenu = GetMenu(hwndMain);\r
2233   RECT crect, wrect, oldRect;\r
2234   int offby;\r
2235   LOGBRUSH logbrush;\r
2236 \r
2237   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2238   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2239 \r
2240   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2241   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2242 \r
2243   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2244   oldRect.top = wpMain.y;\r
2245   oldRect.right = wpMain.x + wpMain.width;\r
2246   oldRect.bottom = wpMain.y + wpMain.height;\r
2247 \r
2248   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2249   smallLayout = sizeInfo[boardSize].smallLayout;\r
2250   squareSize = sizeInfo[boardSize].squareSize;\r
2251   lineGap = sizeInfo[boardSize].lineGap;\r
2252   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2253 \r
2254   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2255       lineGap = appData.overrideLineGap;\r
2256   }\r
2257 \r
2258   if (tinyLayout != oldTinyLayout) {\r
2259     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2260     if (tinyLayout) {\r
2261       style &= ~WS_SYSMENU;\r
2262       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2263                  "&Minimize\tCtrl+F4");\r
2264     } else {\r
2265       style |= WS_SYSMENU;\r
2266       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2267     }\r
2268     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2269 \r
2270     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2271       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2272         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2273     }\r
2274     DrawMenuBar(hwndMain);\r
2275   }\r
2276 \r
2277   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2278   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2279 \r
2280   /* Get text area sizes */\r
2281   hdc = GetDC(hwndMain);\r
2282   if (appData.clockMode) {\r
2283     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2284   } else {\r
2285     snprintf(buf, MSG_SIZ, _("White"));\r
2286   }\r
2287   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2288   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2289   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2290   str = _("We only care about the height here");\r
2291   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2292   SelectObject(hdc, oldFont);\r
2293   ReleaseDC(hwndMain, hdc);\r
2294 \r
2295   /* Compute where everything goes */\r
2296   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2297         /* [HGM] logo: if either logo is on, reserve space for it */\r
2298         logoHeight =  2*clockSize.cy;\r
2299         leftLogoRect.left   = OUTER_MARGIN;\r
2300         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2301         leftLogoRect.top    = OUTER_MARGIN;\r
2302         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2303 \r
2304         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2305         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2306         rightLogoRect.top    = OUTER_MARGIN;\r
2307         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2308 \r
2309 \r
2310     whiteRect.left = leftLogoRect.right;\r
2311     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2312     whiteRect.top = OUTER_MARGIN;\r
2313     whiteRect.bottom = whiteRect.top + logoHeight;\r
2314 \r
2315     blackRect.right = rightLogoRect.left;\r
2316     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2317     blackRect.top = whiteRect.top;\r
2318     blackRect.bottom = whiteRect.bottom;\r
2319   } else {\r
2320     whiteRect.left = OUTER_MARGIN;\r
2321     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2322     whiteRect.top = OUTER_MARGIN;\r
2323     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2324 \r
2325     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2326     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2327     blackRect.top = whiteRect.top;\r
2328     blackRect.bottom = whiteRect.bottom;\r
2329 \r
2330     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2331   }\r
2332 \r
2333   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2334   if (appData.showButtonBar) {\r
2335     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2336       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2337   } else {\r
2338     messageRect.right = OUTER_MARGIN + boardWidth;\r
2339   }\r
2340   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2341   messageRect.bottom = messageRect.top + messageSize.cy;\r
2342 \r
2343   boardRect.left = OUTER_MARGIN;\r
2344   boardRect.right = boardRect.left + boardWidth;\r
2345   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2346   boardRect.bottom = boardRect.top + boardHeight;\r
2347 \r
2348   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2349   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2350   oldBoardSize = boardSize;\r
2351   oldTinyLayout = tinyLayout;\r
2352   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2353   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2354     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2355   winW *= 1 + twoBoards;\r
2356   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2357   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2358   wpMain.height = winH; //       without disturbing window attachments\r
2359   GetWindowRect(hwndMain, &wrect);\r
2360   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2361                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2362 \r
2363   // [HGM] placement: let attached windows follow size change.\r
2364   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2365   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2366   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2367   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2368   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2369 \r
2370   /* compensate if menu bar wrapped */\r
2371   GetClientRect(hwndMain, &crect);\r
2372   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2373   wpMain.height += offby;\r
2374   switch (flags) {\r
2375   case WMSZ_TOPLEFT:\r
2376     SetWindowPos(hwndMain, NULL, \r
2377                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2378                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2379     break;\r
2380 \r
2381   case WMSZ_TOPRIGHT:\r
2382   case WMSZ_TOP:\r
2383     SetWindowPos(hwndMain, NULL, \r
2384                  wrect.left, wrect.bottom - wpMain.height, \r
2385                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2386     break;\r
2387 \r
2388   case WMSZ_BOTTOMLEFT:\r
2389   case WMSZ_LEFT:\r
2390     SetWindowPos(hwndMain, NULL, \r
2391                  wrect.right - wpMain.width, wrect.top, \r
2392                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2393     break;\r
2394 \r
2395   case WMSZ_BOTTOMRIGHT:\r
2396   case WMSZ_BOTTOM:\r
2397   case WMSZ_RIGHT:\r
2398   default:\r
2399     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2400                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2401     break;\r
2402   }\r
2403 \r
2404   hwndPause = NULL;\r
2405   for (i = 0; i < N_BUTTONS; i++) {\r
2406     if (buttonDesc[i].hwnd != NULL) {\r
2407       DestroyWindow(buttonDesc[i].hwnd);\r
2408       buttonDesc[i].hwnd = NULL;\r
2409     }\r
2410     if (appData.showButtonBar) {\r
2411       buttonDesc[i].hwnd =\r
2412         CreateWindow("BUTTON", buttonDesc[i].label,\r
2413                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2414                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2415                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2416                      (HMENU) buttonDesc[i].id,\r
2417                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2418       if (tinyLayout) {\r
2419         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2420                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2421                     MAKELPARAM(FALSE, 0));\r
2422       }\r
2423       if (buttonDesc[i].id == IDM_Pause)\r
2424         hwndPause = buttonDesc[i].hwnd;\r
2425       buttonDesc[i].wndproc = (WNDPROC)\r
2426         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2427     }\r
2428   }\r
2429   if (gridPen != NULL) DeleteObject(gridPen);\r
2430   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2431   if (premovePen != NULL) DeleteObject(premovePen);\r
2432   if (lineGap != 0) {\r
2433     logbrush.lbStyle = BS_SOLID;\r
2434     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2435     gridPen =\r
2436       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2437                    lineGap, &logbrush, 0, NULL);\r
2438     logbrush.lbColor = highlightSquareColor;\r
2439     highlightPen =\r
2440       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2441                    lineGap, &logbrush, 0, NULL);\r
2442 \r
2443     logbrush.lbColor = premoveHighlightColor; \r
2444     premovePen =\r
2445       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2446                    lineGap, &logbrush, 0, NULL);\r
2447 \r
2448     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2449     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2450       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2451       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2452         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2453       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2454         BOARD_WIDTH * (squareSize + lineGap);\r
2455       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2456     }\r
2457     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2458       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2459       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2460         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2461         lineGap / 2 + (i * (squareSize + lineGap));\r
2462       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2463         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2464       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2465     }\r
2466   }\r
2467 \r
2468   /* [HGM] Licensing requirement */\r
2469 #ifdef GOTHIC\r
2470   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2471 #endif\r
2472 #ifdef FALCON\r
2473   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2474 #endif\r
2475   GothicPopUp( "", VariantNormal);\r
2476 \r
2477 \r
2478 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2479 \r
2480   /* Load piece bitmaps for this board size */\r
2481   for (i=0; i<=2; i++) {\r
2482     for (piece = WhitePawn;\r
2483          (int) piece < (int) BlackPawn;\r
2484          piece = (ChessSquare) ((int) piece + 1)) {\r
2485       if (pieceBitmap[i][piece] != NULL)\r
2486         DeleteObject(pieceBitmap[i][piece]);\r
2487     }\r
2488   }\r
2489 \r
2490   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2491   // Orthodox Chess pieces\r
2492   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2493   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2494   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2495   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2496   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2497   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2498   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2499   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2500   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2501   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2502   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2503   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2504   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2505   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2506   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2507   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2508     // in Shogi, Hijack the unused Queen for Lance\r
2509     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2510     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2511     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2512   } else {\r
2513     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2514     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2515     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2516   }\r
2517 \r
2518   if(squareSize <= 72 && squareSize >= 33) { \r
2519     /* A & C are available in most sizes now */\r
2520     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2521       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2522       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2523       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2524       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2525       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2526       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2527       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2528       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2529       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2530       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2531       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2532       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2533     } else { // Smirf-like\r
2534       if(gameInfo.variant == VariantSChess) {\r
2535         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2536         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2537         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2538       } else {\r
2539         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2540         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2541         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2542       }\r
2543     }\r
2544     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2545       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2546       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2547       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2548     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2549       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2550       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2551       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2552     } else { // WinBoard standard\r
2553       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2554       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2555       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2556     }\r
2557   }\r
2558 \r
2559 \r
2560   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2561     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2562     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2563     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2564     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2565     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2566     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2567     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2568     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2569     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2570     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2571     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2572     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2573     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2574     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2575     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2576     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2577     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2578     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2579     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2580     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2581     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2582     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2583     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2584     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2585     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2586     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2587     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2588     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2589     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2590     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2591 \r
2592     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2593       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2594       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2595       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2596       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2597       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2598       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2599       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2600       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2601       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2602       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2603       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2604       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2605     } else {\r
2606       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2607       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2608       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2609       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2610       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2611       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2612       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2613       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2614       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2615       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2616       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2617       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2618     }\r
2619 \r
2620   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2621     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2622     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2623     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2624     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2625     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2626     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2627     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2628     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2629     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2630     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2631     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2632     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2633     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2634     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2635   }\r
2636 \r
2637 \r
2638   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2639   /* special Shogi support in this size */\r
2640   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2641       for (piece = WhitePawn;\r
2642            (int) piece < (int) BlackPawn;\r
2643            piece = (ChessSquare) ((int) piece + 1)) {\r
2644         if (pieceBitmap[i][piece] != NULL)\r
2645           DeleteObject(pieceBitmap[i][piece]);\r
2646       }\r
2647     }\r
2648   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2649   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2650   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2651   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2652   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2653   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2654   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2655   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2656   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2657   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2658   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2659   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2660   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2661   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2662   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2663   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2664   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2665   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2666   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2667   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2668   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2669   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2670   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2671   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2672   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2673   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2674   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2675   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2676   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2677   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2678   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2679   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2680   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2681   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2682   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2683   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2684   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2685   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2686   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2687   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2688   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2689   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2690   minorSize = 0;\r
2691   }\r
2692 }\r
2693 \r
2694 HBITMAP\r
2695 PieceBitmap(ChessSquare p, int kind)\r
2696 {\r
2697   if ((int) p >= (int) BlackPawn)\r
2698     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2699 \r
2700   return pieceBitmap[kind][(int) p];\r
2701 }\r
2702 \r
2703 /***************************************************************/\r
2704 \r
2705 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2706 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2707 /*\r
2708 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2709 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2710 */\r
2711 \r
2712 VOID\r
2713 SquareToPos(int row, int column, int * x, int * y)\r
2714 {\r
2715   if (flipView) {\r
2716     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2717     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2718   } else {\r
2719     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2720     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2721   }\r
2722 }\r
2723 \r
2724 VOID\r
2725 DrawCoordsOnDC(HDC hdc)\r
2726 {\r
2727   static char files[] = "0123456789012345678901221098765432109876543210";\r
2728   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2729   char str[2] = { NULLCHAR, NULLCHAR };\r
2730   int oldMode, oldAlign, x, y, start, i;\r
2731   HFONT oldFont;\r
2732   HBRUSH oldBrush;\r
2733 \r
2734   if (!appData.showCoords)\r
2735     return;\r
2736 \r
2737   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2738 \r
2739   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2740   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2741   oldAlign = GetTextAlign(hdc);\r
2742   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2743 \r
2744   y = boardRect.top + lineGap;\r
2745   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2746 \r
2747   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2748   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2749     str[0] = files[start + i];\r
2750     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2751     y += squareSize + lineGap;\r
2752   }\r
2753 \r
2754   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2755 \r
2756   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2757   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2758     str[0] = ranks[start + i];\r
2759     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2760     x += squareSize + lineGap;\r
2761   }    \r
2762 \r
2763   SelectObject(hdc, oldBrush);\r
2764   SetBkMode(hdc, oldMode);\r
2765   SetTextAlign(hdc, oldAlign);\r
2766   SelectObject(hdc, oldFont);\r
2767 }\r
2768 \r
2769 VOID\r
2770 DrawGridOnDC(HDC hdc)\r
2771 {\r
2772   HPEN oldPen;\r
2773  \r
2774   if (lineGap != 0) {\r
2775     oldPen = SelectObject(hdc, gridPen);\r
2776     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2777     SelectObject(hdc, oldPen);\r
2778   }\r
2779 }\r
2780 \r
2781 #define HIGHLIGHT_PEN 0\r
2782 #define PREMOVE_PEN   1\r
2783 \r
2784 VOID\r
2785 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2786 {\r
2787   int x1, y1;\r
2788   HPEN oldPen, hPen;\r
2789   if (lineGap == 0) return;\r
2790   if (flipView) {\r
2791     x1 = boardRect.left +\r
2792       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2793     y1 = boardRect.top +\r
2794       lineGap/2 + y * (squareSize + lineGap);\r
2795   } else {\r
2796     x1 = boardRect.left +\r
2797       lineGap/2 + x * (squareSize + lineGap);\r
2798     y1 = boardRect.top +\r
2799       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2800   }\r
2801   hPen = pen ? premovePen : highlightPen;\r
2802   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2803   MoveToEx(hdc, x1, y1, NULL);\r
2804   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2805   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2806   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2807   LineTo(hdc, x1, y1);\r
2808   SelectObject(hdc, oldPen);\r
2809 }\r
2810 \r
2811 VOID\r
2812 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2813 {\r
2814   int i;\r
2815   for (i=0; i<2; i++) {\r
2816     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2817       DrawHighlightOnDC(hdc, TRUE,\r
2818                         h->sq[i].x, h->sq[i].y,\r
2819                         pen);\r
2820   }\r
2821 }\r
2822 \r
2823 /* Note: sqcolor is used only in monoMode */\r
2824 /* Note that this code is largely duplicated in woptions.c,\r
2825    function DrawSampleSquare, so that needs to be updated too */\r
2826 VOID\r
2827 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2828 {\r
2829   HBITMAP oldBitmap;\r
2830   HBRUSH oldBrush;\r
2831   int tmpSize;\r
2832 \r
2833   if (appData.blindfold) return;\r
2834 \r
2835   /* [AS] Use font-based pieces if needed */\r
2836   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2837     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2838     CreatePiecesFromFont();\r
2839 \r
2840     if( fontBitmapSquareSize == squareSize ) {\r
2841         int index = TranslatePieceToFontPiece(piece);\r
2842 \r
2843         SelectObject( tmphdc, hPieceMask[ index ] );\r
2844 \r
2845       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2846         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2847       else\r
2848         BitBlt( hdc,\r
2849             x, y,\r
2850             squareSize, squareSize,\r
2851             tmphdc,\r
2852             0, 0,\r
2853             SRCAND );\r
2854 \r
2855         SelectObject( tmphdc, hPieceFace[ index ] );\r
2856 \r
2857       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2858         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2859       else\r
2860         BitBlt( hdc,\r
2861             x, y,\r
2862             squareSize, squareSize,\r
2863             tmphdc,\r
2864             0, 0,\r
2865             SRCPAINT );\r
2866 \r
2867         return;\r
2868     }\r
2869   }\r
2870 \r
2871   if (appData.monoMode) {\r
2872     SelectObject(tmphdc, PieceBitmap(piece, \r
2873       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2874     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2875            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2876   } else {\r
2877     tmpSize = squareSize;\r
2878     if(minorSize &&\r
2879         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2880          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2881       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2882       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2883       x += (squareSize - minorSize)>>1;\r
2884       y += squareSize - minorSize - 2;\r
2885       tmpSize = minorSize;\r
2886     }\r
2887     if (color || appData.allWhite ) {\r
2888       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2889       if( color )\r
2890               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2891       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2892       if(appData.upsideDown && color==flipView)\r
2893         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2894       else\r
2895         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2896       /* Use black for outline of white pieces */\r
2897       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2898       if(appData.upsideDown && color==flipView)\r
2899         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2900       else\r
2901         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2902     } else {\r
2903       /* Use square color for details of black pieces */\r
2904       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2905       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2906       if(appData.upsideDown && !flipView)\r
2907         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2908       else\r
2909         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2910     }\r
2911     SelectObject(hdc, oldBrush);\r
2912     SelectObject(tmphdc, oldBitmap);\r
2913   }\r
2914 }\r
2915 \r
2916 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2917 int GetBackTextureMode( int algo )\r
2918 {\r
2919     int result = BACK_TEXTURE_MODE_DISABLED;\r
2920 \r
2921     switch( algo ) \r
2922     {\r
2923         case BACK_TEXTURE_MODE_PLAIN:\r
2924             result = 1; /* Always use identity map */\r
2925             break;\r
2926         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2927             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2928             break;\r
2929     }\r
2930 \r
2931     return result;\r
2932 }\r
2933 \r
2934 /* \r
2935     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2936     to handle redraws cleanly (as random numbers would always be different).\r
2937 */\r
2938 VOID RebuildTextureSquareInfo()\r
2939 {\r
2940     BITMAP bi;\r
2941     int lite_w = 0;\r
2942     int lite_h = 0;\r
2943     int dark_w = 0;\r
2944     int dark_h = 0;\r
2945     int row;\r
2946     int col;\r
2947 \r
2948     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2949 \r
2950     if( liteBackTexture != NULL ) {\r
2951         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2952             lite_w = bi.bmWidth;\r
2953             lite_h = bi.bmHeight;\r
2954         }\r
2955     }\r
2956 \r
2957     if( darkBackTexture != NULL ) {\r
2958         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2959             dark_w = bi.bmWidth;\r
2960             dark_h = bi.bmHeight;\r
2961         }\r
2962     }\r
2963 \r
2964     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2965         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2966             if( (col + row) & 1 ) {\r
2967                 /* Lite square */\r
2968                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2969                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2970                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2971                   else\r
2972                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2973                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2974                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2975                   else\r
2976                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2977                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2978                 }\r
2979             }\r
2980             else {\r
2981                 /* Dark square */\r
2982                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2983                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2984                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2985                   else\r
2986                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2987                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2988                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2989                   else\r
2990                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2991                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2992                 }\r
2993             }\r
2994         }\r
2995     }\r
2996 }\r
2997 \r
2998 /* [AS] Arrow highlighting support */\r
2999 \r
3000 static double A_WIDTH = 5; /* Width of arrow body */\r
3001 \r
3002 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3003 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3004 \r
3005 static double Sqr( double x )\r
3006 {\r
3007     return x*x;\r
3008 }\r
3009 \r
3010 static int Round( double x )\r
3011 {\r
3012     return (int) (x + 0.5);\r
3013 }\r
3014 \r
3015 /* Draw an arrow between two points using current settings */\r
3016 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3017 {\r
3018     POINT arrow[7];\r
3019     double dx, dy, j, k, x, y;\r
3020 \r
3021     if( d_x == s_x ) {\r
3022         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3023 \r
3024         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3025         arrow[0].y = s_y;\r
3026 \r
3027         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3028         arrow[1].y = d_y - h;\r
3029 \r
3030         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3031         arrow[2].y = d_y - h;\r
3032 \r
3033         arrow[3].x = d_x;\r
3034         arrow[3].y = d_y;\r
3035 \r
3036         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3037         arrow[5].y = d_y - h;\r
3038 \r
3039         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3040         arrow[4].y = d_y - h;\r
3041 \r
3042         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3043         arrow[6].y = s_y;\r
3044     }\r
3045     else if( d_y == s_y ) {\r
3046         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3047 \r
3048         arrow[0].x = s_x;\r
3049         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3050 \r
3051         arrow[1].x = d_x - w;\r
3052         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3053 \r
3054         arrow[2].x = d_x - w;\r
3055         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3056 \r
3057         arrow[3].x = d_x;\r
3058         arrow[3].y = d_y;\r
3059 \r
3060         arrow[5].x = d_x - w;\r
3061         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3062 \r
3063         arrow[4].x = d_x - w;\r
3064         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3065 \r
3066         arrow[6].x = s_x;\r
3067         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3068     }\r
3069     else {\r
3070         /* [AS] Needed a lot of paper for this! :-) */\r
3071         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3072         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3073   \r
3074         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3075 \r
3076         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3077 \r
3078         x = s_x;\r
3079         y = s_y;\r
3080 \r
3081         arrow[0].x = Round(x - j);\r
3082         arrow[0].y = Round(y + j*dx);\r
3083 \r
3084         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3085         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3086 \r
3087         if( d_x > s_x ) {\r
3088             x = (double) d_x - k;\r
3089             y = (double) d_y - k*dy;\r
3090         }\r
3091         else {\r
3092             x = (double) d_x + k;\r
3093             y = (double) d_y + k*dy;\r
3094         }\r
3095 \r
3096         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3097 \r
3098         arrow[6].x = Round(x - j);\r
3099         arrow[6].y = Round(y + j*dx);\r
3100 \r
3101         arrow[2].x = Round(arrow[6].x + 2*j);\r
3102         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3103 \r
3104         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3105         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3106 \r
3107         arrow[4].x = d_x;\r
3108         arrow[4].y = d_y;\r
3109 \r
3110         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3111         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3112     }\r
3113 \r
3114     Polygon( hdc, arrow, 7 );\r
3115 }\r
3116 \r
3117 /* [AS] Draw an arrow between two squares */\r
3118 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3119 {\r
3120     int s_x, s_y, d_x, d_y;\r
3121     HPEN hpen;\r
3122     HPEN holdpen;\r
3123     HBRUSH hbrush;\r
3124     HBRUSH holdbrush;\r
3125     LOGBRUSH stLB;\r
3126 \r
3127     if( s_col == d_col && s_row == d_row ) {\r
3128         return;\r
3129     }\r
3130 \r
3131     /* Get source and destination points */\r
3132     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3133     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3134 \r
3135     if( d_y > s_y ) {\r
3136         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3137     }\r
3138     else if( d_y < s_y ) {\r
3139         d_y += squareSize / 2 + squareSize / 4;\r
3140     }\r
3141     else {\r
3142         d_y += squareSize / 2;\r
3143     }\r
3144 \r
3145     if( d_x > s_x ) {\r
3146         d_x += squareSize / 2 - squareSize / 4;\r
3147     }\r
3148     else if( d_x < s_x ) {\r
3149         d_x += squareSize / 2 + squareSize / 4;\r
3150     }\r
3151     else {\r
3152         d_x += squareSize / 2;\r
3153     }\r
3154 \r
3155     s_x += squareSize / 2;\r
3156     s_y += squareSize / 2;\r
3157 \r
3158     /* Adjust width */\r
3159     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3160 \r
3161     /* Draw */\r
3162     stLB.lbStyle = BS_SOLID;\r
3163     stLB.lbColor = appData.highlightArrowColor;\r
3164     stLB.lbHatch = 0;\r
3165 \r
3166     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3167     holdpen = SelectObject( hdc, hpen );\r
3168     hbrush = CreateBrushIndirect( &stLB );\r
3169     holdbrush = SelectObject( hdc, hbrush );\r
3170 \r
3171     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3172 \r
3173     SelectObject( hdc, holdpen );\r
3174     SelectObject( hdc, holdbrush );\r
3175     DeleteObject( hpen );\r
3176     DeleteObject( hbrush );\r
3177 }\r
3178 \r
3179 BOOL HasHighlightInfo()\r
3180 {\r
3181     BOOL result = FALSE;\r
3182 \r
3183     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3184         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3185     {\r
3186         result = TRUE;\r
3187     }\r
3188 \r
3189     return result;\r
3190 }\r
3191 \r
3192 BOOL IsDrawArrowEnabled()\r
3193 {\r
3194     BOOL result = FALSE;\r
3195 \r
3196     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3197         result = TRUE;\r
3198     }\r
3199 \r
3200     return result;\r
3201 }\r
3202 \r
3203 VOID DrawArrowHighlight( HDC hdc )\r
3204 {\r
3205     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3206         DrawArrowBetweenSquares( hdc,\r
3207             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3208             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3209     }\r
3210 }\r
3211 \r
3212 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3213 {\r
3214     HRGN result = NULL;\r
3215 \r
3216     if( HasHighlightInfo() ) {\r
3217         int x1, y1, x2, y2;\r
3218         int sx, sy, dx, dy;\r
3219 \r
3220         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3221         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3222 \r
3223         sx = MIN( x1, x2 );\r
3224         sy = MIN( y1, y2 );\r
3225         dx = MAX( x1, x2 ) + squareSize;\r
3226         dy = MAX( y1, y2 ) + squareSize;\r
3227 \r
3228         result = CreateRectRgn( sx, sy, dx, dy );\r
3229     }\r
3230 \r
3231     return result;\r
3232 }\r
3233 \r
3234 /*\r
3235     Warning: this function modifies the behavior of several other functions. \r
3236     \r
3237     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3238     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3239     repaint is scattered all over the place, which is not good for features such as\r
3240     "arrow highlighting" that require a full repaint of the board.\r
3241 \r
3242     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3243     user interaction, when speed is not so important) but especially to avoid errors\r
3244     in the displayed graphics.\r
3245 \r
3246     In such patched places, I always try refer to this function so there is a single\r
3247     place to maintain knowledge.\r
3248     \r
3249     To restore the original behavior, just return FALSE unconditionally.\r
3250 */\r
3251 BOOL IsFullRepaintPreferrable()\r
3252 {\r
3253     BOOL result = FALSE;\r
3254 \r
3255     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3256         /* Arrow may appear on the board */\r
3257         result = TRUE;\r
3258     }\r
3259 \r
3260     return result;\r
3261 }\r
3262 \r
3263 /* \r
3264     This function is called by DrawPosition to know whether a full repaint must\r
3265     be forced or not.\r
3266 \r
3267     Only DrawPosition may directly call this function, which makes use of \r
3268     some state information. Other function should call DrawPosition specifying \r
3269     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3270 */\r
3271 BOOL DrawPositionNeedsFullRepaint()\r
3272 {\r
3273     BOOL result = FALSE;\r
3274 \r
3275     /* \r
3276         Probably a slightly better policy would be to trigger a full repaint\r
3277         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3278         but animation is fast enough that it's difficult to notice.\r
3279     */\r
3280     if( animInfo.piece == EmptySquare ) {\r
3281         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3282             result = TRUE;\r
3283         }\r
3284     }\r
3285 \r
3286     return result;\r
3287 }\r
3288 \r
3289 VOID\r
3290 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3291 {\r
3292   int row, column, x, y, square_color, piece_color;\r
3293   ChessSquare piece;\r
3294   HBRUSH oldBrush;\r
3295   HDC texture_hdc = NULL;\r
3296 \r
3297   /* [AS] Initialize background textures if needed */\r
3298   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3299       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3300       if( backTextureSquareSize != squareSize \r
3301        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3302           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3303           backTextureSquareSize = squareSize;\r
3304           RebuildTextureSquareInfo();\r
3305       }\r
3306 \r
3307       texture_hdc = CreateCompatibleDC( hdc );\r
3308   }\r
3309 \r
3310   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3311     for (column = 0; column < BOARD_WIDTH; column++) {\r
3312   \r
3313       SquareToPos(row, column, &x, &y);\r
3314 \r
3315       piece = board[row][column];\r
3316 \r
3317       square_color = ((column + row) % 2) == 1;\r
3318       if( gameInfo.variant == VariantXiangqi ) {\r
3319           square_color = !InPalace(row, column);\r
3320           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3321           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3322       }\r
3323       piece_color = (int) piece < (int) BlackPawn;\r
3324 \r
3325 \r
3326       /* [HGM] holdings file: light square or black */\r
3327       if(column == BOARD_LEFT-2) {\r
3328             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3329                 square_color = 1;\r
3330             else {\r
3331                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3332                 continue;\r
3333             }\r
3334       } else\r
3335       if(column == BOARD_RGHT + 1 ) {\r
3336             if( row < gameInfo.holdingsSize )\r
3337                 square_color = 1;\r
3338             else {\r
3339                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3340                 continue;\r
3341             }\r
3342       }\r
3343       if(column == BOARD_LEFT-1 ) /* left align */\r
3344             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3345       else if( column == BOARD_RGHT) /* right align */\r
3346             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3347       else\r
3348       if (appData.monoMode) {\r
3349         if (piece == EmptySquare) {\r
3350           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3351                  square_color ? WHITENESS : BLACKNESS);\r
3352         } else {\r
3353           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3354         }\r
3355       } \r
3356       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3357           /* [AS] Draw the square using a texture bitmap */\r
3358           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3359           int r = row, c = column; // [HGM] do not flip board in flipView\r
3360           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3361 \r
3362           DrawTile( x, y, \r
3363               squareSize, squareSize, \r
3364               hdc, \r
3365               texture_hdc,\r
3366               backTextureSquareInfo[r][c].mode,\r
3367               backTextureSquareInfo[r][c].x,\r
3368               backTextureSquareInfo[r][c].y );\r
3369 \r
3370           SelectObject( texture_hdc, hbm );\r
3371 \r
3372           if (piece != EmptySquare) {\r
3373               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3374           }\r
3375       }\r
3376       else {\r
3377         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3378 \r
3379         oldBrush = SelectObject(hdc, brush );\r
3380         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3381         SelectObject(hdc, oldBrush);\r
3382         if (piece != EmptySquare)\r
3383           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3384       }\r
3385     }\r
3386   }\r
3387 \r
3388   if( texture_hdc != NULL ) {\r
3389     DeleteDC( texture_hdc );\r
3390   }\r
3391 }\r
3392 \r
3393 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3394 void fputDW(FILE *f, int x)\r
3395 {\r
3396         fputc(x     & 255, f);\r
3397         fputc(x>>8  & 255, f);\r
3398         fputc(x>>16 & 255, f);\r
3399         fputc(x>>24 & 255, f);\r
3400 }\r
3401 \r
3402 #define MAX_CLIPS 200   /* more than enough */\r
3403 \r
3404 VOID\r
3405 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3406 {\r
3407 //  HBITMAP bufferBitmap;\r
3408   BITMAP bi;\r
3409 //  RECT Rect;\r
3410   HDC tmphdc;\r
3411   HBITMAP hbm;\r
3412   int w = 100, h = 50;\r
3413 \r
3414   if(logo == NULL) {\r
3415     if(!logoHeight) return;\r
3416     FillRect( hdc, &logoRect, whitePieceBrush );\r
3417   }\r
3418 //  GetClientRect(hwndMain, &Rect);\r
3419 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3420 //                                      Rect.bottom-Rect.top+1);\r
3421   tmphdc = CreateCompatibleDC(hdc);\r
3422   hbm = SelectObject(tmphdc, logo);\r
3423   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3424             w = bi.bmWidth;\r
3425             h = bi.bmHeight;\r
3426   }\r
3427   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3428                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3429   SelectObject(tmphdc, hbm);\r
3430   DeleteDC(tmphdc);\r
3431 }\r
3432 \r
3433 VOID\r
3434 DisplayLogos()\r
3435 {\r
3436   if(logoHeight) {\r
3437         HDC hdc = GetDC(hwndMain);\r
3438         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3439         if(appData.autoLogo) {\r
3440           \r
3441           switch(gameMode) { // pick logos based on game mode\r
3442             case IcsObserving:\r
3443                 whiteLogo = second.programLogo; // ICS logo\r
3444                 blackLogo = second.programLogo;\r
3445             default:\r
3446                 break;\r
3447             case IcsPlayingWhite:\r
3448                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3449                 blackLogo = second.programLogo; // ICS logo\r
3450                 break;\r
3451             case IcsPlayingBlack:\r
3452                 whiteLogo = second.programLogo; // ICS logo\r
3453                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3454                 break;\r
3455             case TwoMachinesPlay:\r
3456                 if(first.twoMachinesColor[0] == 'b') {\r
3457                     whiteLogo = second.programLogo;\r
3458                     blackLogo = first.programLogo;\r
3459                 }\r
3460                 break;\r
3461             case MachinePlaysWhite:\r
3462                 blackLogo = userLogo;\r
3463                 break;\r
3464             case MachinePlaysBlack:\r
3465                 whiteLogo = userLogo;\r
3466                 blackLogo = first.programLogo;\r
3467           }\r
3468         }\r
3469         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3470         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3471         ReleaseDC(hwndMain, hdc);\r
3472   }\r
3473 }\r
3474 \r
3475 void\r
3476 UpdateLogos(int display)\r
3477 { // called after loading new engine(s), in tourney or from menu\r
3478   LoadLogo(&first, 0, FALSE);\r
3479   LoadLogo(&second, 1, appData.icsActive);\r
3480   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3481   if(display) DisplayLogos();\r
3482 }\r
3483 \r
3484 static HDC hdcSeek;\r
3485 \r
3486 // [HGM] seekgraph\r
3487 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3488 {\r
3489     POINT stPt;\r
3490     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3491     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3492     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3493     SelectObject( hdcSeek, hp );\r
3494 }\r
3495 \r
3496 // front-end wrapper for drawing functions to do rectangles\r
3497 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3498 {\r
3499     HPEN hp;\r
3500     RECT rc;\r
3501 \r
3502     if (hdcSeek == NULL) {\r
3503     hdcSeek = GetDC(hwndMain);\r
3504       if (!appData.monoMode) {\r
3505         SelectPalette(hdcSeek, hPal, FALSE);\r
3506         RealizePalette(hdcSeek);\r
3507       }\r
3508     }\r
3509     hp = SelectObject( hdcSeek, gridPen );\r
3510     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3511     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3512     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3513     SelectObject( hdcSeek, hp );\r
3514 }\r
3515 \r
3516 // front-end wrapper for putting text in graph\r
3517 void DrawSeekText(char *buf, int x, int y)\r
3518 {\r
3519         SIZE stSize;\r
3520         SetBkMode( hdcSeek, TRANSPARENT );\r
3521         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3522         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3523 }\r
3524 \r
3525 void DrawSeekDot(int x, int y, int color)\r
3526 {\r
3527         int square = color & 0x80;\r
3528         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3529                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3530         color &= 0x7F;\r
3531         if(square)\r
3532             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3533                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3534         else\r
3535             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3536                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3537             SelectObject(hdcSeek, oldBrush);\r
3538 }\r
3539 \r
3540 VOID\r
3541 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3542 {\r
3543   static Board lastReq[2], lastDrawn[2];\r
3544   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3545   static int lastDrawnFlipView = 0;\r
3546   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3547   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3548   HDC tmphdc;\r
3549   HDC hdcmem;\r
3550   HBITMAP bufferBitmap;\r
3551   HBITMAP oldBitmap;\r
3552   RECT Rect;\r
3553   HRGN clips[MAX_CLIPS];\r
3554   ChessSquare dragged_piece = EmptySquare;\r
3555   int nr = twoBoards*partnerUp;\r
3556 \r
3557   /* I'm undecided on this - this function figures out whether a full\r
3558    * repaint is necessary on its own, so there's no real reason to have the\r
3559    * caller tell it that.  I think this can safely be set to FALSE - but\r
3560    * if we trust the callers not to request full repaints unnessesarily, then\r
3561    * we could skip some clipping work.  In other words, only request a full\r
3562    * redraw when the majority of pieces have changed positions (ie. flip, \r
3563    * gamestart and similar)  --Hawk\r
3564    */\r
3565   Boolean fullrepaint = repaint;\r
3566 \r
3567   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3568 \r
3569   if( DrawPositionNeedsFullRepaint() ) {\r
3570       fullrepaint = TRUE;\r
3571   }\r
3572 \r
3573   if (board == NULL) {\r
3574     if (!lastReqValid[nr]) {\r
3575       return;\r
3576     }\r
3577     board = lastReq[nr];\r
3578   } else {\r
3579     CopyBoard(lastReq[nr], board);\r
3580     lastReqValid[nr] = 1;\r
3581   }\r
3582 \r
3583   if (doingSizing) {\r
3584     return;\r
3585   }\r
3586 \r
3587   if (IsIconic(hwndMain)) {\r
3588     return;\r
3589   }\r
3590 \r
3591   if (hdc == NULL) {\r
3592     hdc = GetDC(hwndMain);\r
3593     if (!appData.monoMode) {\r
3594       SelectPalette(hdc, hPal, FALSE);\r
3595       RealizePalette(hdc);\r
3596     }\r
3597     releaseDC = TRUE;\r
3598   } else {\r
3599     releaseDC = FALSE;\r
3600   }\r
3601 \r
3602   /* Create some work-DCs */\r
3603   hdcmem = CreateCompatibleDC(hdc);\r
3604   tmphdc = CreateCompatibleDC(hdc);\r
3605 \r
3606   /* If dragging is in progress, we temporarely remove the piece */\r
3607   /* [HGM] or temporarily decrease count if stacked              */\r
3608   /*       !! Moved to before board compare !!                   */\r
3609   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3610     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3611     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3612             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3613         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3614     } else \r
3615     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3616             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3617         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3618     } else \r
3619         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3620   }\r
3621 \r
3622   /* Figure out which squares need updating by comparing the \r
3623    * newest board with the last drawn board and checking if\r
3624    * flipping has changed.\r
3625    */\r
3626   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3627     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3628       for (column = 0; column < BOARD_WIDTH; column++) {\r
3629         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3630           SquareToPos(row, column, &x, &y);\r
3631           clips[num_clips++] =\r
3632             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3633         }\r
3634       }\r
3635     }\r
3636    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3637     for (i=0; i<2; i++) {\r
3638       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3639           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3640         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3641             lastDrawnHighlight.sq[i].y >= 0) {\r
3642           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3643                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3644           clips[num_clips++] =\r
3645             CreateRectRgn(x - lineGap, y - lineGap, \r
3646                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3647         }\r
3648         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3649           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3650           clips[num_clips++] =\r
3651             CreateRectRgn(x - lineGap, y - lineGap, \r
3652                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3653         }\r
3654       }\r
3655     }\r
3656     for (i=0; i<2; i++) {\r
3657       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3658           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3659         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3660             lastDrawnPremove.sq[i].y >= 0) {\r
3661           SquareToPos(lastDrawnPremove.sq[i].y,\r
3662                       lastDrawnPremove.sq[i].x, &x, &y);\r
3663           clips[num_clips++] =\r
3664             CreateRectRgn(x - lineGap, y - lineGap, \r
3665                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3666         }\r
3667         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3668             premoveHighlightInfo.sq[i].y >= 0) {\r
3669           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3670                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3671           clips[num_clips++] =\r
3672             CreateRectRgn(x - lineGap, y - lineGap, \r
3673                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3674         }\r
3675       }\r
3676     }\r
3677    } else { // nr == 1\r
3678         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3679         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3680         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3681         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3682       for (i=0; i<2; i++) {\r
3683         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3684             partnerHighlightInfo.sq[i].y >= 0) {\r
3685           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3686                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3687           clips[num_clips++] =\r
3688             CreateRectRgn(x - lineGap, y - lineGap, \r
3689                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3690         }\r
3691         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3692             oldPartnerHighlight.sq[i].y >= 0) {\r
3693           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3694                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3695           clips[num_clips++] =\r
3696             CreateRectRgn(x - lineGap, y - lineGap, \r
3697                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3698         }\r
3699       }\r
3700    }\r
3701   } else {\r
3702     fullrepaint = TRUE;\r
3703   }\r
3704 \r
3705   /* Create a buffer bitmap - this is the actual bitmap\r
3706    * being written to.  When all the work is done, we can\r
3707    * copy it to the real DC (the screen).  This avoids\r
3708    * the problems with flickering.\r
3709    */\r
3710   GetClientRect(hwndMain, &Rect);\r
3711   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3712                                         Rect.bottom-Rect.top+1);\r
3713   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3714   if (!appData.monoMode) {\r
3715     SelectPalette(hdcmem, hPal, FALSE);\r
3716   }\r
3717 \r
3718   /* Create clips for dragging */\r
3719   if (!fullrepaint) {\r
3720     if (dragInfo.from.x >= 0) {\r
3721       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3722       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3723     }\r
3724     if (dragInfo.start.x >= 0) {\r
3725       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3726       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3727     }\r
3728     if (dragInfo.pos.x >= 0) {\r
3729       x = dragInfo.pos.x - squareSize / 2;\r
3730       y = dragInfo.pos.y - squareSize / 2;\r
3731       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3732     }\r
3733     if (dragInfo.lastpos.x >= 0) {\r
3734       x = dragInfo.lastpos.x - squareSize / 2;\r
3735       y = dragInfo.lastpos.y - squareSize / 2;\r
3736       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3737     }\r
3738   }\r
3739 \r
3740   /* Are we animating a move?  \r
3741    * If so, \r
3742    *   - remove the piece from the board (temporarely)\r
3743    *   - calculate the clipping region\r
3744    */\r
3745   if (!fullrepaint) {\r
3746     if (animInfo.piece != EmptySquare) {\r
3747       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3748       x = boardRect.left + animInfo.lastpos.x;\r
3749       y = boardRect.top + animInfo.lastpos.y;\r
3750       x2 = boardRect.left + animInfo.pos.x;\r
3751       y2 = boardRect.top + animInfo.pos.y;\r
3752       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3753       /* Slight kludge.  The real problem is that after AnimateMove is\r
3754          done, the position on the screen does not match lastDrawn.\r
3755          This currently causes trouble only on e.p. captures in\r
3756          atomic, where the piece moves to an empty square and then\r
3757          explodes.  The old and new positions both had an empty square\r
3758          at the destination, but animation has drawn a piece there and\r
3759          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3760       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3761     }\r
3762   }\r
3763 \r
3764   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3765   if (num_clips == 0)\r
3766     fullrepaint = TRUE;\r
3767 \r
3768   /* Set clipping on the memory DC */\r
3769   if (!fullrepaint) {\r
3770     SelectClipRgn(hdcmem, clips[0]);\r
3771     for (x = 1; x < num_clips; x++) {\r
3772       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3773         abort();  // this should never ever happen!\r
3774     }\r
3775   }\r
3776 \r
3777   /* Do all the drawing to the memory DC */\r
3778   if(explodeInfo.radius) { // [HGM] atomic\r
3779         HBRUSH oldBrush;\r
3780         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3781         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3782         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3783         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3784         x += squareSize/2;\r
3785         y += squareSize/2;\r
3786         if(!fullrepaint) {\r
3787           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3788           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3789         }\r
3790         DrawGridOnDC(hdcmem);\r
3791         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3792         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3793         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3794         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3795         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3796         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3797         SelectObject(hdcmem, oldBrush);\r
3798   } else {\r
3799     DrawGridOnDC(hdcmem);\r
3800     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3801         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3802         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3803     } else {\r
3804         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3805         oldPartnerHighlight = partnerHighlightInfo;\r
3806     }\r
3807     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3808   }\r
3809   if(nr == 0) // [HGM] dual: markers only on left board\r
3810   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3811     for (column = 0; column < BOARD_WIDTH; column++) {\r
3812         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3813             HBRUSH oldBrush = SelectObject(hdcmem, \r
3814                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3815             SquareToPos(row, column, &x, &y);\r
3816             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3817                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3818             SelectObject(hdcmem, oldBrush);\r
3819         }\r
3820     }\r
3821   }\r
3822 \r
3823   if( appData.highlightMoveWithArrow ) {\r
3824     DrawArrowHighlight(hdcmem);\r
3825   }\r
3826 \r
3827   DrawCoordsOnDC(hdcmem);\r
3828 \r
3829   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3830                  /* to make sure lastDrawn contains what is actually drawn */\r
3831 \r
3832   /* Put the dragged piece back into place and draw it (out of place!) */\r
3833     if (dragged_piece != EmptySquare) {\r
3834     /* [HGM] or restack */\r
3835     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3836                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3837     else\r
3838     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3839                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3840     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3841     x = dragInfo.pos.x - squareSize / 2;\r
3842     y = dragInfo.pos.y - squareSize / 2;\r
3843     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3844                   ((int) dragInfo.piece < (int) BlackPawn), \r
3845                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3846   }   \r
3847   \r
3848   /* Put the animated piece back into place and draw it */\r
3849   if (animInfo.piece != EmptySquare) {\r
3850     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3851     x = boardRect.left + animInfo.pos.x;\r
3852     y = boardRect.top + animInfo.pos.y;\r
3853     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3854                   ((int) animInfo.piece < (int) BlackPawn),\r
3855                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3856   }\r
3857 \r
3858   /* Release the bufferBitmap by selecting in the old bitmap \r
3859    * and delete the memory DC\r
3860    */\r
3861   SelectObject(hdcmem, oldBitmap);\r
3862   DeleteDC(hdcmem);\r
3863 \r
3864   /* Set clipping on the target DC */\r
3865   if (!fullrepaint) {\r
3866     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3867         RECT rect;\r
3868         GetRgnBox(clips[x], &rect);\r
3869         DeleteObject(clips[x]);\r
3870         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3871                           rect.right + wpMain.width/2, rect.bottom);\r
3872     }\r
3873     SelectClipRgn(hdc, clips[0]);\r
3874     for (x = 1; x < num_clips; x++) {\r
3875       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3876         abort();   // this should never ever happen!\r
3877     } \r
3878   }\r
3879 \r
3880   /* Copy the new bitmap onto the screen in one go.\r
3881    * This way we avoid any flickering\r
3882    */\r
3883   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3884   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3885          boardRect.right - boardRect.left,\r
3886          boardRect.bottom - boardRect.top,\r
3887          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3888   if(saveDiagFlag) { \r
3889     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3890     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3891 \r
3892     GetObject(bufferBitmap, sizeof(b), &b);\r
3893     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3894         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3895         bih.biWidth = b.bmWidth;\r
3896         bih.biHeight = b.bmHeight;\r
3897         bih.biPlanes = 1;\r
3898         bih.biBitCount = b.bmBitsPixel;\r
3899         bih.biCompression = 0;\r
3900         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3901         bih.biXPelsPerMeter = 0;\r
3902         bih.biYPelsPerMeter = 0;\r
3903         bih.biClrUsed = 0;\r
3904         bih.biClrImportant = 0;\r
3905 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3906 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3907         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3908 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3909 \r
3910         wb = b.bmWidthBytes;\r
3911         // count colors\r
3912         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3913                 int k = ((int*) pData)[i];\r
3914                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3915                 if(j >= 16) break;\r
3916                 color[j] = k;\r
3917                 if(j >= nrColors) nrColors = j+1;\r
3918         }\r
3919         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3920                 INT p = 0;\r
3921                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3922                     for(w=0; w<(wb>>2); w+=2) {\r
3923                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3924                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3925                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3926                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3927                         pData[p++] = m | j<<4;\r
3928                     }\r
3929                     while(p&3) pData[p++] = 0;\r
3930                 }\r
3931                 fac = 3;\r
3932                 wb = ((wb+31)>>5)<<2;\r
3933         }\r
3934         // write BITMAPFILEHEADER\r
3935         fprintf(diagFile, "BM");\r
3936         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3937         fputDW(diagFile, 0);\r
3938         fputDW(diagFile, 0x36 + (fac?64:0));\r
3939         // write BITMAPINFOHEADER\r
3940         fputDW(diagFile, 40);\r
3941         fputDW(diagFile, b.bmWidth);\r
3942         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3943         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3944         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\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         fputDW(diagFile, 0);\r
3951         // write color table\r
3952         if(fac)\r
3953         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3954         // write bitmap data\r
3955         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3956                 fputc(pData[i], diagFile);\r
3957         free(pData);\r
3958      }\r
3959   }\r
3960 \r
3961   SelectObject(tmphdc, oldBitmap);\r
3962 \r
3963   /* Massive cleanup */\r
3964   for (x = 0; x < num_clips; x++)\r
3965     DeleteObject(clips[x]);\r
3966 \r
3967   DeleteDC(tmphdc);\r
3968   DeleteObject(bufferBitmap);\r
3969 \r
3970   if (releaseDC) \r
3971     ReleaseDC(hwndMain, hdc);\r
3972   \r
3973   if (lastDrawnFlipView != flipView && nr == 0) {\r
3974     if (flipView)\r
3975       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3976     else\r
3977       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3978   }\r
3979 \r
3980 /*  CopyBoard(lastDrawn, board);*/\r
3981   lastDrawnHighlight = highlightInfo;\r
3982   lastDrawnPremove   = premoveHighlightInfo;\r
3983   lastDrawnFlipView = flipView;\r
3984   lastDrawnValid[nr] = 1;\r
3985 }\r
3986 \r
3987 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3988 int\r
3989 SaveDiagram(f)\r
3990      FILE *f;\r
3991 {\r
3992     saveDiagFlag = 1; diagFile = f;\r
3993     HDCDrawPosition(NULL, TRUE, NULL);\r
3994     saveDiagFlag = 0;\r
3995 \r
3996     fclose(f);\r
3997     return TRUE;\r
3998 }\r
3999 \r
4000 \r
4001 /*---------------------------------------------------------------------------*\\r
4002 | CLIENT PAINT PROCEDURE\r
4003 |   This is the main event-handler for the WM_PAINT message.\r
4004 |\r
4005 \*---------------------------------------------------------------------------*/\r
4006 VOID\r
4007 PaintProc(HWND hwnd)\r
4008 {\r
4009   HDC         hdc;\r
4010   PAINTSTRUCT ps;\r
4011   HFONT       oldFont;\r
4012 \r
4013   if((hdc = BeginPaint(hwnd, &ps))) {\r
4014     if (IsIconic(hwnd)) {\r
4015       DrawIcon(hdc, 2, 2, iconCurrent);\r
4016     } else {\r
4017       if (!appData.monoMode) {\r
4018         SelectPalette(hdc, hPal, FALSE);\r
4019         RealizePalette(hdc);\r
4020       }\r
4021       HDCDrawPosition(hdc, 1, NULL);\r
4022       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4023         flipView = !flipView; partnerUp = !partnerUp;\r
4024         HDCDrawPosition(hdc, 1, NULL);\r
4025         flipView = !flipView; partnerUp = !partnerUp;\r
4026       }\r
4027       oldFont =\r
4028         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4029       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4030                  ETO_CLIPPED|ETO_OPAQUE,\r
4031                  &messageRect, messageText, strlen(messageText), NULL);\r
4032       SelectObject(hdc, oldFont);\r
4033       DisplayBothClocks();\r
4034       DisplayLogos();\r
4035     }\r
4036     EndPaint(hwnd,&ps);\r
4037   }\r
4038 \r
4039   return;\r
4040 }\r
4041 \r
4042 \r
4043 /*\r
4044  * If the user selects on a border boundary, return -1; if off the board,\r
4045  *   return -2.  Otherwise map the event coordinate to the square.\r
4046  * The offset boardRect.left or boardRect.top must already have been\r
4047  *   subtracted from x.\r
4048  */\r
4049 int EventToSquare(x, limit)\r
4050      int x, limit;\r
4051 {\r
4052   if (x <= 0)\r
4053     return -2;\r
4054   if (x < lineGap)\r
4055     return -1;\r
4056   x -= lineGap;\r
4057   if ((x % (squareSize + lineGap)) >= squareSize)\r
4058     return -1;\r
4059   x /= (squareSize + lineGap);\r
4060     if (x >= limit)\r
4061     return -2;\r
4062   return x;\r
4063 }\r
4064 \r
4065 typedef struct {\r
4066   char piece;\r
4067   int command;\r
4068   char* name;\r
4069 } DropEnable;\r
4070 \r
4071 DropEnable dropEnables[] = {\r
4072   { 'P', DP_Pawn, N_("Pawn") },\r
4073   { 'N', DP_Knight, N_("Knight") },\r
4074   { 'B', DP_Bishop, N_("Bishop") },\r
4075   { 'R', DP_Rook, N_("Rook") },\r
4076   { 'Q', DP_Queen, N_("Queen") },\r
4077 };\r
4078 \r
4079 VOID\r
4080 SetupDropMenu(HMENU hmenu)\r
4081 {\r
4082   int i, count, enable;\r
4083   char *p;\r
4084   extern char white_holding[], black_holding[];\r
4085   char item[MSG_SIZ];\r
4086 \r
4087   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4088     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4089                dropEnables[i].piece);\r
4090     count = 0;\r
4091     while (p && *p++ == dropEnables[i].piece) count++;\r
4092       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4093     enable = count > 0 || !appData.testLegality\r
4094       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4095                       && !appData.icsActive);\r
4096     ModifyMenu(hmenu, dropEnables[i].command,\r
4097                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4098                dropEnables[i].command, item);\r
4099   }\r
4100 }\r
4101 \r
4102 void DragPieceBegin(int x, int y, Boolean instantly)\r
4103 {\r
4104       dragInfo.lastpos.x = boardRect.left + x;\r
4105       dragInfo.lastpos.y = boardRect.top + y;\r
4106       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4107       dragInfo.from.x = fromX;\r
4108       dragInfo.from.y = fromY;\r
4109       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4110       dragInfo.start = dragInfo.from;\r
4111       SetCapture(hwndMain);\r
4112 }\r
4113 \r
4114 void DragPieceEnd(int x, int y)\r
4115 {\r
4116     ReleaseCapture();\r
4117     dragInfo.start.x = dragInfo.start.y = -1;\r
4118     dragInfo.from = dragInfo.start;\r
4119     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4120 }\r
4121 \r
4122 void ChangeDragPiece(ChessSquare piece)\r
4123 {\r
4124     dragInfo.piece = piece;\r
4125 }\r
4126 \r
4127 /* Event handler for mouse messages */\r
4128 VOID\r
4129 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4130 {\r
4131   int x, y, menuNr;\r
4132   POINT pt;\r
4133   static int recursive = 0;\r
4134   HMENU hmenu;\r
4135   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4136 \r
4137   if (recursive) {\r
4138     if (message == WM_MBUTTONUP) {\r
4139       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4140          to the middle button: we simulate pressing the left button too!\r
4141          */\r
4142       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4143       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4144     }\r
4145     return;\r
4146   }\r
4147   recursive++;\r
4148   \r
4149   pt.x = LOWORD(lParam);\r
4150   pt.y = HIWORD(lParam);\r
4151   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4152   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4153   if (!flipView && y >= 0) {\r
4154     y = BOARD_HEIGHT - 1 - y;\r
4155   }\r
4156   if (flipView && x >= 0) {\r
4157     x = BOARD_WIDTH - 1 - x;\r
4158   }\r
4159 \r
4160   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4161 \r
4162   switch (message) {\r
4163   case WM_LBUTTONDOWN:\r
4164       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4165         ClockClick(flipClock); break;\r
4166       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4167         ClockClick(!flipClock); break;\r
4168       }\r
4169       dragInfo.start.x = dragInfo.start.y = -1;\r
4170       dragInfo.from = dragInfo.start;\r
4171     if(fromX == -1 && frozen) { // not sure where this is for\r
4172                 fromX = fromY = -1; \r
4173       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4174       break;\r
4175     }\r
4176       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4177       DrawPosition(TRUE, NULL);\r
4178     break;\r
4179 \r
4180   case WM_LBUTTONUP:\r
4181       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4182       DrawPosition(TRUE, NULL);\r
4183     break;\r
4184 \r
4185   case WM_MOUSEMOVE:\r
4186     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4187     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4188     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4189     if ((appData.animateDragging || appData.highlightDragging)\r
4190         && (wParam & MK_LBUTTON)\r
4191         && dragInfo.from.x >= 0) \r
4192     {\r
4193       BOOL full_repaint = FALSE;\r
4194 \r
4195       if (appData.animateDragging) {\r
4196         dragInfo.pos = pt;\r
4197       }\r
4198       if (appData.highlightDragging) {\r
4199         SetHighlights(fromX, fromY, x, y);\r
4200         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4201             full_repaint = TRUE;\r
4202         }\r
4203       }\r
4204       \r
4205       DrawPosition( full_repaint, NULL);\r
4206       \r
4207       dragInfo.lastpos = dragInfo.pos;\r
4208     }\r
4209     break;\r
4210 \r
4211   case WM_MOUSEWHEEL: // [DM]\r
4212     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4213        /* Mouse Wheel is being rolled forward\r
4214         * Play moves forward\r
4215         */\r
4216        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4217                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4218        /* Mouse Wheel is being rolled backward\r
4219         * Play moves backward\r
4220         */\r
4221        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4222                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4223     }\r
4224     break;\r
4225 \r
4226   case WM_MBUTTONUP:\r
4227   case WM_RBUTTONUP:\r
4228     ReleaseCapture();\r
4229     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4230     break;\r
4231  \r
4232   case WM_MBUTTONDOWN:\r
4233   case WM_RBUTTONDOWN:\r
4234     ErrorPopDown();\r
4235     ReleaseCapture();\r
4236     fromX = fromY = -1;\r
4237     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4238     dragInfo.start.x = dragInfo.start.y = -1;\r
4239     dragInfo.from = dragInfo.start;\r
4240     dragInfo.lastpos = dragInfo.pos;\r
4241     if (appData.highlightDragging) {\r
4242       ClearHighlights();\r
4243     }\r
4244     if(y == -2) {\r
4245       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4246       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4247           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4248       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4249           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4250       }\r
4251       break;\r
4252     }\r
4253     DrawPosition(TRUE, NULL);\r
4254 \r
4255     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4256     switch (menuNr) {\r
4257     case 0:\r
4258       if (message == WM_MBUTTONDOWN) {\r
4259         buttonCount = 3;  /* even if system didn't think so */\r
4260         if (wParam & MK_SHIFT) \r
4261           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4262         else\r
4263           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4264       } else { /* message == WM_RBUTTONDOWN */\r
4265         /* Just have one menu, on the right button.  Windows users don't\r
4266            think to try the middle one, and sometimes other software steals\r
4267            it, or it doesn't really exist. */\r
4268         if(gameInfo.variant != VariantShogi)\r
4269             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4270         else\r
4271             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4272       }\r
4273       break;\r
4274     case 2:\r
4275       SetCapture(hwndMain);\r
4276       break;\r
4277     case 1:\r
4278       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4279       SetupDropMenu(hmenu);\r
4280       MenuPopup(hwnd, pt, hmenu, -1);\r
4281     default:\r
4282       break;\r
4283     }\r
4284     break;\r
4285   }\r
4286 \r
4287   recursive--;\r
4288 }\r
4289 \r
4290 /* Preprocess messages for buttons in main window */\r
4291 LRESULT CALLBACK\r
4292 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4293 {\r
4294   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4295   int i, dir;\r
4296 \r
4297   for (i=0; i<N_BUTTONS; i++) {\r
4298     if (buttonDesc[i].id == id) break;\r
4299   }\r
4300   if (i == N_BUTTONS) return 0;\r
4301   switch (message) {\r
4302   case WM_KEYDOWN:\r
4303     switch (wParam) {\r
4304     case VK_LEFT:\r
4305     case VK_RIGHT:\r
4306       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4307       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4308       return TRUE;\r
4309     }\r
4310     break;\r
4311   case WM_CHAR:\r
4312     switch (wParam) {\r
4313     case '\r':\r
4314       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4315       return TRUE;\r
4316     default:\r
4317       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4318         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4319         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4320         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4321         SetFocus(h);\r
4322         SendMessage(h, WM_CHAR, wParam, lParam);\r
4323         return TRUE;\r
4324       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4325         TypeInEvent((char)wParam);\r
4326       }\r
4327       break;\r
4328     }\r
4329     break;\r
4330   }\r
4331   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4332 }\r
4333 \r
4334 /* Process messages for Promotion dialog box */\r
4335 LRESULT CALLBACK\r
4336 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4337 {\r
4338   char promoChar;\r
4339 \r
4340   switch (message) {\r
4341   case WM_INITDIALOG: /* message: initialize dialog box */\r
4342     /* Center the dialog over the application window */\r
4343     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4344     Translate(hDlg, DLG_PromotionKing);\r
4345     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4346       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4347        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4348        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4349                SW_SHOW : SW_HIDE);\r
4350     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4351     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4352        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4353          PieceToChar(WhiteAngel) != '~') ||\r
4354         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4355          PieceToChar(BlackAngel) != '~')   ) ?\r
4356                SW_SHOW : SW_HIDE);\r
4357     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4358        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4359          PieceToChar(WhiteMarshall) != '~') ||\r
4360         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4361          PieceToChar(BlackMarshall) != '~')   ) ?\r
4362                SW_SHOW : SW_HIDE);\r
4363     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4364     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4365        gameInfo.variant != VariantShogi ?\r
4366                SW_SHOW : SW_HIDE);\r
4367     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4368        gameInfo.variant != VariantShogi ?\r
4369                SW_SHOW : SW_HIDE);\r
4370     if(gameInfo.variant == VariantShogi) {\r
4371         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4372         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4373         SetWindowText(hDlg, "Promote?");\r
4374     }\r
4375     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4376        gameInfo.variant == VariantSuper ?\r
4377                SW_SHOW : SW_HIDE);\r
4378     return TRUE;\r
4379 \r
4380   case WM_COMMAND: /* message: received a command */\r
4381     switch (LOWORD(wParam)) {\r
4382     case IDCANCEL:\r
4383       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4384       ClearHighlights();\r
4385       DrawPosition(FALSE, NULL);\r
4386       return TRUE;\r
4387     case PB_King:\r
4388       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4389       break;\r
4390     case PB_Queen:\r
4391       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4392       break;\r
4393     case PB_Rook:\r
4394       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4395       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4396       break;\r
4397     case PB_Bishop:\r
4398       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4399       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4400       break;\r
4401     case PB_Chancellor:\r
4402       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4403       break;\r
4404     case PB_Archbishop:\r
4405       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4406       break;\r
4407     case PB_Knight:\r
4408       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4409       break;\r
4410     default:\r
4411       return FALSE;\r
4412     }\r
4413     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4414     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4415     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4416     fromX = fromY = -1;\r
4417     if (!appData.highlightLastMove) {\r
4418       ClearHighlights();\r
4419       DrawPosition(FALSE, NULL);\r
4420     }\r
4421     return TRUE;\r
4422   }\r
4423   return FALSE;\r
4424 }\r
4425 \r
4426 /* Pop up promotion dialog */\r
4427 VOID\r
4428 PromotionPopup(HWND hwnd)\r
4429 {\r
4430   FARPROC lpProc;\r
4431 \r
4432   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4433   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4434     hwnd, (DLGPROC)lpProc);\r
4435   FreeProcInstance(lpProc);\r
4436 }\r
4437 \r
4438 void\r
4439 PromotionPopUp()\r
4440 {\r
4441   DrawPosition(TRUE, NULL);\r
4442   PromotionPopup(hwndMain);\r
4443 }\r
4444 \r
4445 /* Toggle ShowThinking */\r
4446 VOID\r
4447 ToggleShowThinking()\r
4448 {\r
4449   appData.showThinking = !appData.showThinking;\r
4450   ShowThinkingEvent();\r
4451 }\r
4452 \r
4453 VOID\r
4454 LoadGameDialog(HWND hwnd, char* title)\r
4455 {\r
4456   UINT number = 0;\r
4457   FILE *f;\r
4458   char fileTitle[MSG_SIZ];\r
4459   f = OpenFileDialog(hwnd, "rb", "",\r
4460                      appData.oldSaveStyle ? "gam" : "pgn",\r
4461                      GAME_FILT,\r
4462                      title, &number, fileTitle, NULL);\r
4463   if (f != NULL) {\r
4464     cmailMsgLoaded = FALSE;\r
4465     if (number == 0) {\r
4466       int error = GameListBuild(f);\r
4467       if (error) {\r
4468         DisplayError(_("Cannot build game list"), error);\r
4469       } else if (!ListEmpty(&gameList) &&\r
4470                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4471         GameListPopUp(f, fileTitle);\r
4472         return;\r
4473       }\r
4474       GameListDestroy();\r
4475       number = 1;\r
4476     }\r
4477     LoadGame(f, number, fileTitle, FALSE);\r
4478   }\r
4479 }\r
4480 \r
4481 int get_term_width()\r
4482 {\r
4483     HDC hdc;\r
4484     TEXTMETRIC tm;\r
4485     RECT rc;\r
4486     HFONT hfont, hold_font;\r
4487     LOGFONT lf;\r
4488     HWND hText;\r
4489 \r
4490     if (hwndConsole)\r
4491         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4492     else\r
4493         return 79;\r
4494 \r
4495     // get the text metrics\r
4496     hdc = GetDC(hText);\r
4497     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4498     if (consoleCF.dwEffects & CFE_BOLD)\r
4499         lf.lfWeight = FW_BOLD;\r
4500     if (consoleCF.dwEffects & CFE_ITALIC)\r
4501         lf.lfItalic = TRUE;\r
4502     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4503         lf.lfStrikeOut = TRUE;\r
4504     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4505         lf.lfUnderline = TRUE;\r
4506     hfont = CreateFontIndirect(&lf);\r
4507     hold_font = SelectObject(hdc, hfont);\r
4508     GetTextMetrics(hdc, &tm);\r
4509     SelectObject(hdc, hold_font);\r
4510     DeleteObject(hfont);\r
4511     ReleaseDC(hText, hdc);\r
4512 \r
4513     // get the rectangle\r
4514     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4515 \r
4516     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4517 }\r
4518 \r
4519 void UpdateICSWidth(HWND hText)\r
4520 {\r
4521     LONG old_width, new_width;\r
4522 \r
4523     new_width = get_term_width(hText, FALSE);\r
4524     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4525     if (new_width != old_width)\r
4526     {\r
4527         ics_update_width(new_width);\r
4528         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4529     }\r
4530 }\r
4531 \r
4532 VOID\r
4533 ChangedConsoleFont()\r
4534 {\r
4535   CHARFORMAT cfmt;\r
4536   CHARRANGE tmpsel, sel;\r
4537   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4538   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4539   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4540   PARAFORMAT paraf;\r
4541 \r
4542   cfmt.cbSize = sizeof(CHARFORMAT);\r
4543   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4544     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4545                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4546   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4547    * size.  This was undocumented in the version of MSVC++ that I had\r
4548    * when I wrote the code, but is apparently documented now.\r
4549    */\r
4550   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4551   cfmt.bCharSet = f->lf.lfCharSet;\r
4552   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4553   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4554   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4555   /* Why are the following seemingly needed too? */\r
4556   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4557   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4558   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4559   tmpsel.cpMin = 0;\r
4560   tmpsel.cpMax = -1; /*999999?*/\r
4561   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4562   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4563   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4564    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4565    */\r
4566   paraf.cbSize = sizeof(paraf);\r
4567   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4568   paraf.dxStartIndent = 0;\r
4569   paraf.dxOffset = WRAP_INDENT;\r
4570   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4571   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4572   UpdateICSWidth(hText);\r
4573 }\r
4574 \r
4575 /*---------------------------------------------------------------------------*\\r
4576  *\r
4577  * Window Proc for main window\r
4578  *\r
4579 \*---------------------------------------------------------------------------*/\r
4580 \r
4581 /* Process messages for main window, etc. */\r
4582 LRESULT CALLBACK\r
4583 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4584 {\r
4585   FARPROC lpProc;\r
4586   int wmId, wmEvent;\r
4587   char *defName;\r
4588   FILE *f;\r
4589   UINT number;\r
4590   char fileTitle[MSG_SIZ];\r
4591   char buf[MSG_SIZ];\r
4592   static SnapData sd;\r
4593   static int peek=0;\r
4594 \r
4595   switch (message) {\r
4596 \r
4597   case WM_PAINT: /* message: repaint portion of window */\r
4598     PaintProc(hwnd);\r
4599     break;\r
4600 \r
4601   case WM_ERASEBKGND:\r
4602     if (IsIconic(hwnd)) {\r
4603       /* Cheat; change the message */\r
4604       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4605     } else {\r
4606       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4607     }\r
4608     break;\r
4609 \r
4610   case WM_LBUTTONDOWN:\r
4611   case WM_MBUTTONDOWN:\r
4612   case WM_RBUTTONDOWN:\r
4613   case WM_LBUTTONUP:\r
4614   case WM_MBUTTONUP:\r
4615   case WM_RBUTTONUP:\r
4616   case WM_MOUSEMOVE:\r
4617   case WM_MOUSEWHEEL:\r
4618     MouseEvent(hwnd, message, wParam, lParam);\r
4619     break;\r
4620 \r
4621   case WM_KEYUP:\r
4622     if((char)wParam == '\b') {\r
4623       ForwardEvent(); peek = 0;\r
4624     }\r
4625 \r
4626     JAWS_KBUP_NAVIGATION\r
4627 \r
4628     break;\r
4629 \r
4630   case WM_KEYDOWN:\r
4631     if((char)wParam == '\b') {\r
4632       if(!peek) BackwardEvent(), peek = 1;\r
4633     }\r
4634 \r
4635     JAWS_KBDOWN_NAVIGATION\r
4636 \r
4637     break;\r
4638 \r
4639   case WM_CHAR:\r
4640     \r
4641     JAWS_ALT_INTERCEPT\r
4642 \r
4643     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4644         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4645         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4646         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4647         SetFocus(h);\r
4648         SendMessage(h, message, wParam, lParam);\r
4649     } else if(lParam != KF_REPEAT) {\r
4650         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4651                 TypeInEvent((char)wParam);\r
4652         } else if((char)wParam == 003) CopyGameToClipboard();\r
4653          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4654     }\r
4655 \r
4656     break;\r
4657 \r
4658   case WM_PALETTECHANGED:\r
4659     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4660       int nnew;\r
4661       HDC hdc = GetDC(hwndMain);\r
4662       SelectPalette(hdc, hPal, TRUE);\r
4663       nnew = RealizePalette(hdc);\r
4664       if (nnew > 0) {\r
4665         paletteChanged = TRUE;\r
4666         InvalidateRect(hwnd, &boardRect, FALSE);\r
4667       }\r
4668       ReleaseDC(hwnd, hdc);\r
4669     }\r
4670     break;\r
4671 \r
4672   case WM_QUERYNEWPALETTE:\r
4673     if (!appData.monoMode /*&& paletteChanged*/) {\r
4674       int nnew;\r
4675       HDC hdc = GetDC(hwndMain);\r
4676       paletteChanged = FALSE;\r
4677       SelectPalette(hdc, hPal, FALSE);\r
4678       nnew = RealizePalette(hdc);\r
4679       if (nnew > 0) {\r
4680         InvalidateRect(hwnd, &boardRect, FALSE);\r
4681       }\r
4682       ReleaseDC(hwnd, hdc);\r
4683       return TRUE;\r
4684     }\r
4685     return FALSE;\r
4686 \r
4687   case WM_COMMAND: /* message: command from application menu */\r
4688     wmId    = LOWORD(wParam);\r
4689     wmEvent = HIWORD(wParam);\r
4690 \r
4691     switch (wmId) {\r
4692     case IDM_NewGame:\r
4693       ResetGameEvent();\r
4694       SAY("new game enter a move to play against the computer with white");\r
4695       break;\r
4696 \r
4697     case IDM_NewGameFRC:\r
4698       if( NewGameFRC() == 0 ) {\r
4699         ResetGameEvent();\r
4700       }\r
4701       break;\r
4702 \r
4703     case IDM_NewVariant:\r
4704       NewVariantPopup(hwnd);\r
4705       break;\r
4706 \r
4707     case IDM_LoadGame:\r
4708       LoadGameDialog(hwnd, _("Load Game from File"));\r
4709       break;\r
4710 \r
4711     case IDM_LoadNextGame:\r
4712       ReloadGame(1);\r
4713       break;\r
4714 \r
4715     case IDM_LoadPrevGame:\r
4716       ReloadGame(-1);\r
4717       break;\r
4718 \r
4719     case IDM_ReloadGame:\r
4720       ReloadGame(0);\r
4721       break;\r
4722 \r
4723     case IDM_LoadPosition:\r
4724       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4725         Reset(FALSE, TRUE);\r
4726       }\r
4727       number = 1;\r
4728       f = OpenFileDialog(hwnd, "rb", "",\r
4729                          appData.oldSaveStyle ? "pos" : "fen",\r
4730                          POSITION_FILT,\r
4731                          _("Load Position from File"), &number, fileTitle, NULL);\r
4732       if (f != NULL) {\r
4733         LoadPosition(f, number, fileTitle);\r
4734       }\r
4735       break;\r
4736 \r
4737     case IDM_LoadNextPosition:\r
4738       ReloadPosition(1);\r
4739       break;\r
4740 \r
4741     case IDM_LoadPrevPosition:\r
4742       ReloadPosition(-1);\r
4743       break;\r
4744 \r
4745     case IDM_ReloadPosition:\r
4746       ReloadPosition(0);\r
4747       break;\r
4748 \r
4749     case IDM_SaveGame:\r
4750       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4751       f = OpenFileDialog(hwnd, "a", defName,\r
4752                          appData.oldSaveStyle ? "gam" : "pgn",\r
4753                          GAME_FILT,\r
4754                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4755       if (f != NULL) {\r
4756         SaveGame(f, 0, "");\r
4757       }\r
4758       break;\r
4759 \r
4760     case IDM_SavePosition:\r
4761       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4762       f = OpenFileDialog(hwnd, "a", defName,\r
4763                          appData.oldSaveStyle ? "pos" : "fen",\r
4764                          POSITION_FILT,\r
4765                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4766       if (f != NULL) {\r
4767         SavePosition(f, 0, "");\r
4768       }\r
4769       break;\r
4770 \r
4771     case IDM_SaveDiagram:\r
4772       defName = "diagram";\r
4773       f = OpenFileDialog(hwnd, "wb", defName,\r
4774                          "bmp",\r
4775                          DIAGRAM_FILT,\r
4776                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4777       if (f != NULL) {\r
4778         SaveDiagram(f);\r
4779       }\r
4780       break;\r
4781 \r
4782     case IDM_CopyGame:\r
4783       CopyGameToClipboard();\r
4784       break;\r
4785 \r
4786     case IDM_PasteGame:\r
4787       PasteGameFromClipboard();\r
4788       break;\r
4789 \r
4790     case IDM_CopyGameListToClipboard:\r
4791       CopyGameListToClipboard();\r
4792       break;\r
4793 \r
4794     /* [AS] Autodetect FEN or PGN data */\r
4795     case IDM_PasteAny:\r
4796       PasteGameOrFENFromClipboard();\r
4797       break;\r
4798 \r
4799     /* [AS] Move history */\r
4800     case IDM_ShowMoveHistory:\r
4801         if( MoveHistoryIsUp() ) {\r
4802             MoveHistoryPopDown();\r
4803         }\r
4804         else {\r
4805             MoveHistoryPopUp();\r
4806         }\r
4807         break;\r
4808 \r
4809     /* [AS] Eval graph */\r
4810     case IDM_ShowEvalGraph:\r
4811         if( EvalGraphIsUp() ) {\r
4812             EvalGraphPopDown();\r
4813         }\r
4814         else {\r
4815             EvalGraphPopUp();\r
4816             SetFocus(hwndMain);\r
4817         }\r
4818         break;\r
4819 \r
4820     /* [AS] Engine output */\r
4821     case IDM_ShowEngineOutput:\r
4822         if( EngineOutputIsUp() ) {\r
4823             EngineOutputPopDown();\r
4824         }\r
4825         else {\r
4826             EngineOutputPopUp();\r
4827         }\r
4828         break;\r
4829 \r
4830     /* [AS] User adjudication */\r
4831     case IDM_UserAdjudication_White:\r
4832         UserAdjudicationEvent( +1 );\r
4833         break;\r
4834 \r
4835     case IDM_UserAdjudication_Black:\r
4836         UserAdjudicationEvent( -1 );\r
4837         break;\r
4838 \r
4839     case IDM_UserAdjudication_Draw:\r
4840         UserAdjudicationEvent( 0 );\r
4841         break;\r
4842 \r
4843     /* [AS] Game list options dialog */\r
4844     case IDM_GameListOptions:\r
4845       GameListOptions();\r
4846       break;\r
4847 \r
4848     case IDM_NewChat:\r
4849       ChatPopUp(NULL);\r
4850       break;\r
4851 \r
4852     case IDM_CopyPosition:\r
4853       CopyFENToClipboard();\r
4854       break;\r
4855 \r
4856     case IDM_PastePosition:\r
4857       PasteFENFromClipboard();\r
4858       break;\r
4859 \r
4860     case IDM_MailMove:\r
4861       MailMoveEvent();\r
4862       break;\r
4863 \r
4864     case IDM_ReloadCMailMsg:\r
4865       Reset(TRUE, TRUE);\r
4866       ReloadCmailMsgEvent(FALSE);\r
4867       break;\r
4868 \r
4869     case IDM_Minimize:\r
4870       ShowWindow(hwnd, SW_MINIMIZE);\r
4871       break;\r
4872 \r
4873     case IDM_Exit:\r
4874       ExitEvent(0);\r
4875       break;\r
4876 \r
4877     case IDM_MachineWhite:\r
4878       MachineWhiteEvent();\r
4879       /*\r
4880        * refresh the tags dialog only if it's visible\r
4881        */\r
4882       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4883           char *tags;\r
4884           tags = PGNTags(&gameInfo);\r
4885           TagsPopUp(tags, CmailMsg());\r
4886           free(tags);\r
4887       }\r
4888       SAY("computer starts playing white");\r
4889       break;\r
4890 \r
4891     case IDM_MachineBlack:\r
4892       MachineBlackEvent();\r
4893       /*\r
4894        * refresh the tags dialog only if it's visible\r
4895        */\r
4896       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4897           char *tags;\r
4898           tags = PGNTags(&gameInfo);\r
4899           TagsPopUp(tags, CmailMsg());\r
4900           free(tags);\r
4901       }\r
4902       SAY("computer starts playing black");\r
4903       break;\r
4904 \r
4905     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4906       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4907       break;\r
4908 \r
4909     case IDM_TwoMachines:\r
4910       TwoMachinesEvent();\r
4911       /*\r
4912        * refresh the tags dialog only if it's visible\r
4913        */\r
4914       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4915           char *tags;\r
4916           tags = PGNTags(&gameInfo);\r
4917           TagsPopUp(tags, CmailMsg());\r
4918           free(tags);\r
4919       }\r
4920       SAY("computer starts playing both sides");\r
4921       break;\r
4922 \r
4923     case IDM_AnalysisMode:\r
4924       if (!first.analysisSupport) {\r
4925         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4926         DisplayError(buf, 0);\r
4927       } else {\r
4928         SAY("analyzing current position");\r
4929         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4930         if (appData.icsActive) {\r
4931                if (gameMode != IcsObserving) {\r
4932                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4933                        DisplayError(buf, 0);\r
4934                        /* secure check */\r
4935                        if (appData.icsEngineAnalyze) {\r
4936                                if (appData.debugMode) \r
4937                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4938                                ExitAnalyzeMode();\r
4939                                ModeHighlight();\r
4940                                break;\r
4941                        }\r
4942                        break;\r
4943                } else {\r
4944                        /* if enable, user want disable icsEngineAnalyze */\r
4945                        if (appData.icsEngineAnalyze) {\r
4946                                ExitAnalyzeMode();\r
4947                                ModeHighlight();\r
4948                                break;\r
4949                        }\r
4950                        appData.icsEngineAnalyze = TRUE;\r
4951                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4952                }\r
4953         } \r
4954         if (!appData.showThinking) ToggleShowThinking();\r
4955         AnalyzeModeEvent();\r
4956       }\r
4957       break;\r
4958 \r
4959     case IDM_AnalyzeFile:\r
4960       if (!first.analysisSupport) {\r
4961         char buf[MSG_SIZ];\r
4962           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4963         DisplayError(buf, 0);\r
4964       } else {\r
4965         if (!appData.showThinking) ToggleShowThinking();\r
4966         AnalyzeFileEvent();\r
4967 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4968         AnalysisPeriodicEvent(1);\r
4969       }\r
4970       break;\r
4971 \r
4972     case IDM_IcsClient:\r
4973       IcsClientEvent();\r
4974       break;\r
4975 \r
4976     case IDM_EditGame:\r
4977     case IDM_EditGame2:\r
4978       EditGameEvent();\r
4979       SAY("edit game");\r
4980       break;\r
4981 \r
4982     case IDM_EditPosition:\r
4983     case IDM_EditPosition2:\r
4984       EditPositionEvent();\r
4985       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4986       break;\r
4987 \r
4988     case IDM_Training:\r
4989       TrainingEvent();\r
4990       break;\r
4991 \r
4992     case IDM_ShowGameList:\r
4993       ShowGameListProc();\r
4994       break;\r
4995 \r
4996     case IDM_EditProgs1:\r
4997       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4998       break;\r
4999 \r
5000     case IDM_LoadProg1:\r
5001      LoadEnginePopUp(hwndMain, 0);\r
5002       break;\r
5003 \r
5004     case IDM_LoadProg2:\r
5005      LoadEnginePopUp(hwndMain, 1);\r
5006       break;\r
5007 \r
5008     case IDM_EditServers:\r
5009       EditTagsPopUp(icsNames, &icsNames);\r
5010       break;\r
5011 \r
5012     case IDM_EditTags:\r
5013     case IDM_Tags:\r
5014       EditTagsProc();\r
5015       break;\r
5016 \r
5017     case IDM_EditBook:\r
5018       EditBookEvent();\r
5019       break;\r
5020 \r
5021     case IDM_EditComment:\r
5022     case IDM_Comment:\r
5023       if (commentUp && editComment) {\r
5024         CommentPopDown();\r
5025       } else {\r
5026         EditCommentEvent();\r
5027       }\r
5028       break;\r
5029 \r
5030     case IDM_Pause:\r
5031       PauseEvent();\r
5032       break;\r
5033 \r
5034     case IDM_Accept:\r
5035       AcceptEvent();\r
5036       break;\r
5037 \r
5038     case IDM_Decline:\r
5039       DeclineEvent();\r
5040       break;\r
5041 \r
5042     case IDM_Rematch:\r
5043       RematchEvent();\r
5044       break;\r
5045 \r
5046     case IDM_CallFlag:\r
5047       CallFlagEvent();\r
5048       break;\r
5049 \r
5050     case IDM_Draw:\r
5051       DrawEvent();\r
5052       break;\r
5053 \r
5054     case IDM_Adjourn:\r
5055       AdjournEvent();\r
5056       break;\r
5057 \r
5058     case IDM_Abort:\r
5059       AbortEvent();\r
5060       break;\r
5061 \r
5062     case IDM_Resign:\r
5063       ResignEvent();\r
5064       break;\r
5065 \r
5066     case IDM_StopObserving:\r
5067       StopObservingEvent();\r
5068       break;\r
5069 \r
5070     case IDM_StopExamining:\r
5071       StopExaminingEvent();\r
5072       break;\r
5073 \r
5074     case IDM_Upload:\r
5075       UploadGameEvent();\r
5076       break;\r
5077 \r
5078     case IDM_TypeInMove:\r
5079       TypeInEvent('\000');\r
5080       break;\r
5081 \r
5082     case IDM_TypeInName:\r
5083       PopUpNameDialog('\000');\r
5084       break;\r
5085 \r
5086     case IDM_Backward:\r
5087       BackwardEvent();\r
5088       SetFocus(hwndMain);\r
5089       break;\r
5090 \r
5091     JAWS_MENU_ITEMS\r
5092 \r
5093     case IDM_Forward:\r
5094       ForwardEvent();\r
5095       SetFocus(hwndMain);\r
5096       break;\r
5097 \r
5098     case IDM_ToStart:\r
5099       ToStartEvent();\r
5100       SetFocus(hwndMain);\r
5101       break;\r
5102 \r
5103     case IDM_ToEnd:\r
5104       ToEndEvent();\r
5105       SetFocus(hwndMain);\r
5106       break;\r
5107 \r
5108     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5109     case OPT_GameListPrev:\r
5110       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5111       break;\r
5112 \r
5113     case IDM_Revert:\r
5114       RevertEvent(FALSE);\r
5115       break;\r
5116 \r
5117     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5118       RevertEvent(TRUE);\r
5119       break;\r
5120 \r
5121     case IDM_TruncateGame:\r
5122       TruncateGameEvent();\r
5123       break;\r
5124 \r
5125     case IDM_MoveNow:\r
5126       MoveNowEvent();\r
5127       break;\r
5128 \r
5129     case IDM_RetractMove:\r
5130       RetractMoveEvent();\r
5131       break;\r
5132 \r
5133     case IDM_FlipView:\r
5134       flipView = !flipView;\r
5135       DrawPosition(FALSE, NULL);\r
5136       break;\r
5137 \r
5138     case IDM_FlipClock:\r
5139       flipClock = !flipClock;\r
5140       DisplayBothClocks();\r
5141       DisplayLogos();\r
5142       break;\r
5143 \r
5144     case IDM_MuteSounds:\r
5145       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5146       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5147                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5148       break;\r
5149 \r
5150     case IDM_GeneralOptions:\r
5151       GeneralOptionsPopup(hwnd);\r
5152       DrawPosition(TRUE, NULL);\r
5153       break;\r
5154 \r
5155     case IDM_BoardOptions:\r
5156       BoardOptionsPopup(hwnd);\r
5157       break;\r
5158 \r
5159     case IDM_EnginePlayOptions:\r
5160       EnginePlayOptionsPopup(hwnd);\r
5161       break;\r
5162 \r
5163     case IDM_Engine1Options:\r
5164       EngineOptionsPopup(hwnd, &first);\r
5165       break;\r
5166 \r
5167     case IDM_Engine2Options:\r
5168       savedHwnd = hwnd;\r
5169       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5170       EngineOptionsPopup(hwnd, &second);\r
5171       break;\r
5172 \r
5173     case IDM_OptionsUCI:\r
5174       UciOptionsPopup(hwnd);\r
5175       break;\r
5176 \r
5177     case IDM_Tourney:\r
5178       TourneyPopup(hwnd);\r
5179       break;\r
5180 \r
5181     case IDM_IcsOptions:\r
5182       IcsOptionsPopup(hwnd);\r
5183       break;\r
5184 \r
5185     case IDM_Fonts:\r
5186       FontsOptionsPopup(hwnd);\r
5187       break;\r
5188 \r
5189     case IDM_Sounds:\r
5190       SoundOptionsPopup(hwnd);\r
5191       break;\r
5192 \r
5193     case IDM_CommPort:\r
5194       CommPortOptionsPopup(hwnd);\r
5195       break;\r
5196 \r
5197     case IDM_LoadOptions:\r
5198       LoadOptionsPopup(hwnd);\r
5199       break;\r
5200 \r
5201     case IDM_SaveOptions:\r
5202       SaveOptionsPopup(hwnd);\r
5203       break;\r
5204 \r
5205     case IDM_TimeControl:\r
5206       TimeControlOptionsPopup(hwnd);\r
5207       break;\r
5208 \r
5209     case IDM_SaveSettings:\r
5210       SaveSettings(settingsFileName);\r
5211       break;\r
5212 \r
5213     case IDM_SaveSettingsOnExit:\r
5214       saveSettingsOnExit = !saveSettingsOnExit;\r
5215       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5216                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5217                                          MF_CHECKED : MF_UNCHECKED));\r
5218       break;\r
5219 \r
5220     case IDM_Hint:\r
5221       HintEvent();\r
5222       break;\r
5223 \r
5224     case IDM_Book:\r
5225       BookEvent();\r
5226       break;\r
5227 \r
5228     case IDM_AboutGame:\r
5229       AboutGameEvent();\r
5230       break;\r
5231 \r
5232     case IDM_Debug:\r
5233       appData.debugMode = !appData.debugMode;\r
5234       if (appData.debugMode) {\r
5235         char dir[MSG_SIZ];\r
5236         GetCurrentDirectory(MSG_SIZ, dir);\r
5237         SetCurrentDirectory(installDir);\r
5238         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5239         SetCurrentDirectory(dir);\r
5240         setbuf(debugFP, NULL);\r
5241       } else {\r
5242         fclose(debugFP);\r
5243         debugFP = NULL;\r
5244       }\r
5245       break;\r
5246 \r
5247     case IDM_HELPCONTENTS:\r
5248       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5249           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5250           MessageBox (GetFocus(),\r
5251                     _("Unable to activate help"),\r
5252                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5253       }\r
5254       break;\r
5255 \r
5256     case IDM_HELPSEARCH:\r
5257         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5258             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5259         MessageBox (GetFocus(),\r
5260                     _("Unable to activate help"),\r
5261                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5262       }\r
5263       break;\r
5264 \r
5265     case IDM_HELPHELP:\r
5266       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5267         MessageBox (GetFocus(),\r
5268                     _("Unable to activate help"),\r
5269                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5270       }\r
5271       break;\r
5272 \r
5273     case IDM_ABOUT:\r
5274       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5275       DialogBox(hInst, \r
5276         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5277         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5278       FreeProcInstance(lpProc);\r
5279       break;\r
5280 \r
5281     case IDM_DirectCommand1:\r
5282       AskQuestionEvent(_("Direct Command"),\r
5283                        _("Send to chess program:"), "", "1");\r
5284       break;\r
5285     case IDM_DirectCommand2:\r
5286       AskQuestionEvent(_("Direct Command"),\r
5287                        _("Send to second chess program:"), "", "2");\r
5288       break;\r
5289 \r
5290     case EP_WhitePawn:\r
5291       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5292       fromX = fromY = -1;\r
5293       break;\r
5294 \r
5295     case EP_WhiteKnight:\r
5296       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5297       fromX = fromY = -1;\r
5298       break;\r
5299 \r
5300     case EP_WhiteBishop:\r
5301       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5302       fromX = fromY = -1;\r
5303       break;\r
5304 \r
5305     case EP_WhiteRook:\r
5306       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5307       fromX = fromY = -1;\r
5308       break;\r
5309 \r
5310     case EP_WhiteQueen:\r
5311       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5312       fromX = fromY = -1;\r
5313       break;\r
5314 \r
5315     case EP_WhiteFerz:\r
5316       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5317       fromX = fromY = -1;\r
5318       break;\r
5319 \r
5320     case EP_WhiteWazir:\r
5321       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5322       fromX = fromY = -1;\r
5323       break;\r
5324 \r
5325     case EP_WhiteAlfil:\r
5326       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5327       fromX = fromY = -1;\r
5328       break;\r
5329 \r
5330     case EP_WhiteCannon:\r
5331       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5332       fromX = fromY = -1;\r
5333       break;\r
5334 \r
5335     case EP_WhiteCardinal:\r
5336       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5337       fromX = fromY = -1;\r
5338       break;\r
5339 \r
5340     case EP_WhiteMarshall:\r
5341       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5342       fromX = fromY = -1;\r
5343       break;\r
5344 \r
5345     case EP_WhiteKing:\r
5346       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5347       fromX = fromY = -1;\r
5348       break;\r
5349 \r
5350     case EP_BlackPawn:\r
5351       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5352       fromX = fromY = -1;\r
5353       break;\r
5354 \r
5355     case EP_BlackKnight:\r
5356       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case EP_BlackBishop:\r
5361       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case EP_BlackRook:\r
5366       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case EP_BlackQueen:\r
5371       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case EP_BlackFerz:\r
5376       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case EP_BlackWazir:\r
5381       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case EP_BlackAlfil:\r
5386       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case EP_BlackCannon:\r
5391       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5392       fromX = fromY = -1;\r
5393       break;\r
5394 \r
5395     case EP_BlackCardinal:\r
5396       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case EP_BlackMarshall:\r
5401       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5402       fromX = fromY = -1;\r
5403       break;\r
5404 \r
5405     case EP_BlackKing:\r
5406       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5407       fromX = fromY = -1;\r
5408       break;\r
5409 \r
5410     case EP_EmptySquare:\r
5411       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5412       fromX = fromY = -1;\r
5413       break;\r
5414 \r
5415     case EP_ClearBoard:\r
5416       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_White:\r
5421       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case EP_Black:\r
5426       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case EP_Promote:\r
5431       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case EP_Demote:\r
5436       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case DP_Pawn:\r
5441       DropMenuEvent(WhitePawn, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case DP_Knight:\r
5446       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case DP_Bishop:\r
5451       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case DP_Rook:\r
5456       DropMenuEvent(WhiteRook, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case DP_Queen:\r
5461       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case IDM_English:\r
5466       barbaric = 0; appData.language = "";\r
5467       TranslateMenus(0);\r
5468       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5469       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5470       lastChecked = wmId;\r
5471       break;\r
5472 \r
5473     default:\r
5474       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5475           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5476       else\r
5477       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5478           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5479           TranslateMenus(0);\r
5480           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5481           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5482           lastChecked = wmId;\r
5483           break;\r
5484       }\r
5485       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5486     }\r
5487     break;\r
5488 \r
5489   case WM_TIMER:\r
5490     switch (wParam) {\r
5491     case CLOCK_TIMER_ID:\r
5492       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5493       clockTimerEvent = 0;\r
5494       DecrementClocks(); /* call into back end */\r
5495       break;\r
5496     case LOAD_GAME_TIMER_ID:\r
5497       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5498       loadGameTimerEvent = 0;\r
5499       AutoPlayGameLoop(); /* call into back end */\r
5500       break;\r
5501     case ANALYSIS_TIMER_ID:\r
5502       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5503                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5504         AnalysisPeriodicEvent(0);\r
5505       } else {\r
5506         KillTimer(hwnd, analysisTimerEvent);\r
5507         analysisTimerEvent = 0;\r
5508       }\r
5509       break;\r
5510     case DELAYED_TIMER_ID:\r
5511       KillTimer(hwnd, delayedTimerEvent);\r
5512       delayedTimerEvent = 0;\r
5513       delayedTimerCallback();\r
5514       break;\r
5515     }\r
5516     break;\r
5517 \r
5518   case WM_USER_Input:\r
5519     InputEvent(hwnd, message, wParam, lParam);\r
5520     break;\r
5521 \r
5522   /* [AS] Also move "attached" child windows */\r
5523   case WM_WINDOWPOSCHANGING:\r
5524 \r
5525     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5526         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5527 \r
5528         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5529             /* Window is moving */\r
5530             RECT rcMain;\r
5531 \r
5532 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5533             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5534             rcMain.right  = wpMain.x + wpMain.width;\r
5535             rcMain.top    = wpMain.y;\r
5536             rcMain.bottom = wpMain.y + wpMain.height;\r
5537             \r
5538             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5539             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5540             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5541             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5542             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5543             wpMain.x = lpwp->x;\r
5544             wpMain.y = lpwp->y;\r
5545         }\r
5546     }\r
5547     break;\r
5548 \r
5549   /* [AS] Snapping */\r
5550   case WM_ENTERSIZEMOVE:\r
5551     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5552     if (hwnd == hwndMain) {\r
5553       doingSizing = TRUE;\r
5554       lastSizing = 0;\r
5555     }\r
5556     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5557     break;\r
5558 \r
5559   case WM_SIZING:\r
5560     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5561     if (hwnd == hwndMain) {\r
5562       lastSizing = wParam;\r
5563     }\r
5564     break;\r
5565 \r
5566   case WM_MOVING:\r
5567     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5568       return OnMoving( &sd, hwnd, wParam, lParam );\r
5569 \r
5570   case WM_EXITSIZEMOVE:\r
5571     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5572     if (hwnd == hwndMain) {\r
5573       RECT client;\r
5574       doingSizing = FALSE;\r
5575       InvalidateRect(hwnd, &boardRect, FALSE);\r
5576       GetClientRect(hwnd, &client);\r
5577       ResizeBoard(client.right, client.bottom, lastSizing);\r
5578       lastSizing = 0;\r
5579       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5580     }\r
5581     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5582     break;\r
5583 \r
5584   case WM_DESTROY: /* message: window being destroyed */\r
5585     PostQuitMessage(0);\r
5586     break;\r
5587 \r
5588   case WM_CLOSE:\r
5589     if (hwnd == hwndMain) {\r
5590       ExitEvent(0);\r
5591     }\r
5592     break;\r
5593 \r
5594   default:      /* Passes it on if unprocessed */\r
5595     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5596   }\r
5597   return 0;\r
5598 }\r
5599 \r
5600 /*---------------------------------------------------------------------------*\\r
5601  *\r
5602  * Misc utility routines\r
5603  *\r
5604 \*---------------------------------------------------------------------------*/\r
5605 \r
5606 /*\r
5607  * Decent random number generator, at least not as bad as Windows\r
5608  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5609  */\r
5610 unsigned int randstate;\r
5611 \r
5612 int\r
5613 myrandom(void)\r
5614 {\r
5615   randstate = randstate * 1664525 + 1013904223;\r
5616   return (int) randstate & 0x7fffffff;\r
5617 }\r
5618 \r
5619 void\r
5620 mysrandom(unsigned int seed)\r
5621 {\r
5622   randstate = seed;\r
5623 }\r
5624 \r
5625 \r
5626 /* \r
5627  * returns TRUE if user selects a different color, FALSE otherwise \r
5628  */\r
5629 \r
5630 BOOL\r
5631 ChangeColor(HWND hwnd, COLORREF *which)\r
5632 {\r
5633   static BOOL firstTime = TRUE;\r
5634   static DWORD customColors[16];\r
5635   CHOOSECOLOR cc;\r
5636   COLORREF newcolor;\r
5637   int i;\r
5638   ColorClass ccl;\r
5639 \r
5640   if (firstTime) {\r
5641     /* Make initial colors in use available as custom colors */\r
5642     /* Should we put the compiled-in defaults here instead? */\r
5643     i = 0;\r
5644     customColors[i++] = lightSquareColor & 0xffffff;\r
5645     customColors[i++] = darkSquareColor & 0xffffff;\r
5646     customColors[i++] = whitePieceColor & 0xffffff;\r
5647     customColors[i++] = blackPieceColor & 0xffffff;\r
5648     customColors[i++] = highlightSquareColor & 0xffffff;\r
5649     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5650 \r
5651     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5652       customColors[i++] = textAttribs[ccl].color;\r
5653     }\r
5654     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5655     firstTime = FALSE;\r
5656   }\r
5657 \r
5658   cc.lStructSize = sizeof(cc);\r
5659   cc.hwndOwner = hwnd;\r
5660   cc.hInstance = NULL;\r
5661   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5662   cc.lpCustColors = (LPDWORD) customColors;\r
5663   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5664 \r
5665   if (!ChooseColor(&cc)) return FALSE;\r
5666 \r
5667   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5668   if (newcolor == *which) return FALSE;\r
5669   *which = newcolor;\r
5670   return TRUE;\r
5671 \r
5672   /*\r
5673   InitDrawingColors();\r
5674   InvalidateRect(hwnd, &boardRect, FALSE);\r
5675   */\r
5676 }\r
5677 \r
5678 BOOLEAN\r
5679 MyLoadSound(MySound *ms)\r
5680 {\r
5681   BOOL ok = FALSE;\r
5682   struct stat st;\r
5683   FILE *f;\r
5684 \r
5685   if (ms->data && ms->flag) free(ms->data);\r
5686   ms->data = NULL;\r
5687 \r
5688   switch (ms->name[0]) {\r
5689   case NULLCHAR:\r
5690     /* Silence */\r
5691     ok = TRUE;\r
5692     break;\r
5693   case '$':\r
5694     /* System sound from Control Panel.  Don't preload here. */\r
5695     ok = TRUE;\r
5696     break;\r
5697   case '!':\r
5698     if (ms->name[1] == NULLCHAR) {\r
5699       /* "!" alone = silence */\r
5700       ok = TRUE;\r
5701     } else {\r
5702       /* Builtin wave resource.  Error if not found. */\r
5703       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5704       if (h == NULL) break;\r
5705       ms->data = (void *)LoadResource(hInst, h);\r
5706       ms->flag = 0; // not maloced, so cannot be freed!\r
5707       if (h == NULL) break;\r
5708       ok = TRUE;\r
5709     }\r
5710     break;\r
5711   default:\r
5712     /* .wav file.  Error if not found. */\r
5713     f = fopen(ms->name, "rb");\r
5714     if (f == NULL) break;\r
5715     if (fstat(fileno(f), &st) < 0) break;\r
5716     ms->data = malloc(st.st_size);\r
5717     ms->flag = 1;\r
5718     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5719     fclose(f);\r
5720     ok = TRUE;\r
5721     break;\r
5722   }\r
5723   if (!ok) {\r
5724     char buf[MSG_SIZ];\r
5725       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5726     DisplayError(buf, GetLastError());\r
5727   }\r
5728   return ok;\r
5729 }\r
5730 \r
5731 BOOLEAN\r
5732 MyPlaySound(MySound *ms)\r
5733 {\r
5734   BOOLEAN ok = FALSE;\r
5735 \r
5736   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5737   switch (ms->name[0]) {\r
5738   case NULLCHAR:\r
5739         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5740     /* Silence */\r
5741     ok = TRUE;\r
5742     break;\r
5743   case '$':\r
5744     /* System sound from Control Panel (deprecated feature).\r
5745        "$" alone or an unset sound name gets default beep (still in use). */\r
5746     if (ms->name[1]) {\r
5747       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5748     }\r
5749     if (!ok) ok = MessageBeep(MB_OK);\r
5750     break; \r
5751   case '!':\r
5752     /* Builtin wave resource, or "!" alone for silence */\r
5753     if (ms->name[1]) {\r
5754       if (ms->data == NULL) return FALSE;\r
5755       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5756     } else {\r
5757       ok = TRUE;\r
5758     }\r
5759     break;\r
5760   default:\r
5761     /* .wav file.  Error if not found. */\r
5762     if (ms->data == NULL) return FALSE;\r
5763     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5764     break;\r
5765   }\r
5766   /* Don't print an error: this can happen innocently if the sound driver\r
5767      is busy; for instance, if another instance of WinBoard is playing\r
5768      a sound at about the same time. */\r
5769   return ok;\r
5770 }\r
5771 \r
5772 \r
5773 LRESULT CALLBACK\r
5774 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5775 {\r
5776   BOOL ok;\r
5777   OPENFILENAME *ofn;\r
5778   static UINT *number; /* gross that this is static */\r
5779 \r
5780   switch (message) {\r
5781   case WM_INITDIALOG: /* message: initialize dialog box */\r
5782     /* Center the dialog over the application window */\r
5783     ofn = (OPENFILENAME *) lParam;\r
5784     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5785       number = (UINT *) ofn->lCustData;\r
5786       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5787     } else {\r
5788       number = NULL;\r
5789     }\r
5790     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5791     Translate(hDlg, 1536);\r
5792     return FALSE;  /* Allow for further processing */\r
5793 \r
5794   case WM_COMMAND:\r
5795     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5796       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5797     }\r
5798     return FALSE;  /* Allow for further processing */\r
5799   }\r
5800   return FALSE;\r
5801 }\r
5802 \r
5803 UINT APIENTRY\r
5804 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5805 {\r
5806   static UINT *number;\r
5807   OPENFILENAME *ofname;\r
5808   OFNOTIFY *ofnot;\r
5809   switch (uiMsg) {\r
5810   case WM_INITDIALOG:\r
5811     Translate(hdlg, DLG_IndexNumber);\r
5812     ofname = (OPENFILENAME *)lParam;\r
5813     number = (UINT *)(ofname->lCustData);\r
5814     break;\r
5815   case WM_NOTIFY:\r
5816     ofnot = (OFNOTIFY *)lParam;\r
5817     if (ofnot->hdr.code == CDN_FILEOK) {\r
5818       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5819     }\r
5820     break;\r
5821   }\r
5822   return 0;\r
5823 }\r
5824 \r
5825 \r
5826 FILE *\r
5827 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5828                char *nameFilt, char *dlgTitle, UINT *number,\r
5829                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5830 {\r
5831   OPENFILENAME openFileName;\r
5832   char buf1[MSG_SIZ];\r
5833   FILE *f;\r
5834 \r
5835   if (fileName == NULL) fileName = buf1;\r
5836   if (defName == NULL) {\r
5837     safeStrCpy(fileName, "*.", 3 );\r
5838     strcat(fileName, defExt);\r
5839   } else {\r
5840     safeStrCpy(fileName, defName, MSG_SIZ );\r
5841   }\r
5842     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5843   if (number) *number = 0;\r
5844 \r
5845   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5846   openFileName.hwndOwner         = hwnd;\r
5847   openFileName.hInstance         = (HANDLE) hInst;\r
5848   openFileName.lpstrFilter       = nameFilt;\r
5849   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5850   openFileName.nMaxCustFilter    = 0L;\r
5851   openFileName.nFilterIndex      = 1L;\r
5852   openFileName.lpstrFile         = fileName;\r
5853   openFileName.nMaxFile          = MSG_SIZ;\r
5854   openFileName.lpstrFileTitle    = fileTitle;\r
5855   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5856   openFileName.lpstrInitialDir   = NULL;\r
5857   openFileName.lpstrTitle        = dlgTitle;\r
5858   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5859     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5860     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5861     | (oldDialog ? 0 : OFN_EXPLORER);\r
5862   openFileName.nFileOffset       = 0;\r
5863   openFileName.nFileExtension    = 0;\r
5864   openFileName.lpstrDefExt       = defExt;\r
5865   openFileName.lCustData         = (LONG) number;\r
5866   openFileName.lpfnHook          = oldDialog ?\r
5867     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5868   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5869 \r
5870   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5871                         GetOpenFileName(&openFileName)) {\r
5872     /* open the file */\r
5873     f = fopen(openFileName.lpstrFile, write);\r
5874     if (f == NULL) {\r
5875       MessageBox(hwnd, _("File open failed"), NULL,\r
5876                  MB_OK|MB_ICONEXCLAMATION);\r
5877       return NULL;\r
5878     }\r
5879   } else {\r
5880     int err = CommDlgExtendedError();\r
5881     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5882     return FALSE;\r
5883   }\r
5884   return f;\r
5885 }\r
5886 \r
5887 \r
5888 \r
5889 VOID APIENTRY\r
5890 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5891 {\r
5892   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5893 \r
5894   /*\r
5895    * Get the first pop-up menu in the menu template. This is the\r
5896    * menu that TrackPopupMenu displays.\r
5897    */\r
5898   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5899   TranslateOneMenu(10, hmenuTrackPopup);\r
5900 \r
5901   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5902 \r
5903   /*\r
5904    * TrackPopup uses screen coordinates, so convert the\r
5905    * coordinates of the mouse click to screen coordinates.\r
5906    */\r
5907   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5908 \r
5909   /* Draw and track the floating pop-up menu. */\r
5910   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5911                  pt.x, pt.y, 0, hwnd, NULL);\r
5912 \r
5913   /* Destroy the menu.*/\r
5914   DestroyMenu(hmenu);\r
5915 }\r
5916    \r
5917 typedef struct {\r
5918   HWND hDlg, hText;\r
5919   int sizeX, sizeY, newSizeX, newSizeY;\r
5920   HDWP hdwp;\r
5921 } ResizeEditPlusButtonsClosure;\r
5922 \r
5923 BOOL CALLBACK\r
5924 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5925 {\r
5926   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5927   RECT rect;\r
5928   POINT pt;\r
5929 \r
5930   if (hChild == cl->hText) return TRUE;\r
5931   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5932   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5933   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5934   ScreenToClient(cl->hDlg, &pt);\r
5935   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5936     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5937   return TRUE;\r
5938 }\r
5939 \r
5940 /* Resize a dialog that has a (rich) edit field filling most of\r
5941    the top, with a row of buttons below */\r
5942 VOID\r
5943 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5944 {\r
5945   RECT rectText;\r
5946   int newTextHeight, newTextWidth;\r
5947   ResizeEditPlusButtonsClosure cl;\r
5948   \r
5949   /*if (IsIconic(hDlg)) return;*/\r
5950   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5951   \r
5952   cl.hdwp = BeginDeferWindowPos(8);\r
5953 \r
5954   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5955   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5956   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5957   if (newTextHeight < 0) {\r
5958     newSizeY += -newTextHeight;\r
5959     newTextHeight = 0;\r
5960   }\r
5961   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5962     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5963 \r
5964   cl.hDlg = hDlg;\r
5965   cl.hText = hText;\r
5966   cl.sizeX = sizeX;\r
5967   cl.sizeY = sizeY;\r
5968   cl.newSizeX = newSizeX;\r
5969   cl.newSizeY = newSizeY;\r
5970   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5971 \r
5972   EndDeferWindowPos(cl.hdwp);\r
5973 }\r
5974 \r
5975 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5976 {\r
5977     RECT    rChild, rParent;\r
5978     int     wChild, hChild, wParent, hParent;\r
5979     int     wScreen, hScreen, xNew, yNew;\r
5980     HDC     hdc;\r
5981 \r
5982     /* Get the Height and Width of the child window */\r
5983     GetWindowRect (hwndChild, &rChild);\r
5984     wChild = rChild.right - rChild.left;\r
5985     hChild = rChild.bottom - rChild.top;\r
5986 \r
5987     /* Get the Height and Width of the parent window */\r
5988     GetWindowRect (hwndParent, &rParent);\r
5989     wParent = rParent.right - rParent.left;\r
5990     hParent = rParent.bottom - rParent.top;\r
5991 \r
5992     /* Get the display limits */\r
5993     hdc = GetDC (hwndChild);\r
5994     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5995     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5996     ReleaseDC(hwndChild, hdc);\r
5997 \r
5998     /* Calculate new X position, then adjust for screen */\r
5999     xNew = rParent.left + ((wParent - wChild) /2);\r
6000     if (xNew < 0) {\r
6001         xNew = 0;\r
6002     } else if ((xNew+wChild) > wScreen) {\r
6003         xNew = wScreen - wChild;\r
6004     }\r
6005 \r
6006     /* Calculate new Y position, then adjust for screen */\r
6007     if( mode == 0 ) {\r
6008         yNew = rParent.top  + ((hParent - hChild) /2);\r
6009     }\r
6010     else {\r
6011         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6012     }\r
6013 \r
6014     if (yNew < 0) {\r
6015         yNew = 0;\r
6016     } else if ((yNew+hChild) > hScreen) {\r
6017         yNew = hScreen - hChild;\r
6018     }\r
6019 \r
6020     /* Set it, and return */\r
6021     return SetWindowPos (hwndChild, NULL,\r
6022                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6023 }\r
6024 \r
6025 /* Center one window over another */\r
6026 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6027 {\r
6028     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6029 }\r
6030 \r
6031 /*---------------------------------------------------------------------------*\\r
6032  *\r
6033  * Startup Dialog functions\r
6034  *\r
6035 \*---------------------------------------------------------------------------*/\r
6036 void\r
6037 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6038 {\r
6039   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6040 \r
6041   while (*cd != NULL) {\r
6042     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6043     cd++;\r
6044   }\r
6045 }\r
6046 \r
6047 void\r
6048 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6049 {\r
6050   char buf1[MAX_ARG_LEN];\r
6051   int len;\r
6052 \r
6053   if (str[0] == '@') {\r
6054     FILE* f = fopen(str + 1, "r");\r
6055     if (f == NULL) {\r
6056       DisplayFatalError(str + 1, errno, 2);\r
6057       return;\r
6058     }\r
6059     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6060     fclose(f);\r
6061     buf1[len] = NULLCHAR;\r
6062     str = buf1;\r
6063   }\r
6064 \r
6065   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6066 \r
6067   for (;;) {\r
6068     char buf[MSG_SIZ];\r
6069     char *end = strchr(str, '\n');\r
6070     if (end == NULL) return;\r
6071     memcpy(buf, str, end - str);\r
6072     buf[end - str] = NULLCHAR;\r
6073     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6074     str = end + 1;\r
6075   }\r
6076 }\r
6077 \r
6078 void\r
6079 SetStartupDialogEnables(HWND hDlg)\r
6080 {\r
6081   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6082     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6083     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6084   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6085     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6086   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6087     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6088   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6089     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6090   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6091     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6092     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6093     IsDlgButtonChecked(hDlg, OPT_View));\r
6094 }\r
6095 \r
6096 char *\r
6097 QuoteForFilename(char *filename)\r
6098 {\r
6099   int dquote, space;\r
6100   dquote = strchr(filename, '"') != NULL;\r
6101   space = strchr(filename, ' ') != NULL;\r
6102   if (dquote || space) {\r
6103     if (dquote) {\r
6104       return "'";\r
6105     } else {\r
6106       return "\"";\r
6107     }\r
6108   } else {\r
6109     return "";\r
6110   }\r
6111 }\r
6112 \r
6113 VOID\r
6114 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6115 {\r
6116   char buf[MSG_SIZ];\r
6117   char *q;\r
6118 \r
6119   InitComboStringsFromOption(hwndCombo, nthnames);\r
6120   q = QuoteForFilename(nthcp);\r
6121     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6122   if (*nthdir != NULLCHAR) {\r
6123     q = QuoteForFilename(nthdir);\r
6124       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6125   }\r
6126   if (*nthcp == NULLCHAR) {\r
6127     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6128   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6129     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6130     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6131   }\r
6132 }\r
6133 \r
6134 LRESULT CALLBACK\r
6135 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6136 {\r
6137   char buf[MSG_SIZ];\r
6138   HANDLE hwndCombo;\r
6139   char *p;\r
6140 \r
6141   switch (message) {\r
6142   case WM_INITDIALOG:\r
6143     /* Center the dialog */\r
6144     CenterWindow (hDlg, GetDesktopWindow());\r
6145     Translate(hDlg, DLG_Startup);\r
6146     /* Initialize the dialog items */\r
6147     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6148                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6149                   firstChessProgramNames);\r
6150     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6151                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6152                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6153     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6154     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6155       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6156     if (*appData.icsHelper != NULLCHAR) {\r
6157       char *q = QuoteForFilename(appData.icsHelper);\r
6158       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6159     }\r
6160     if (*appData.icsHost == NULLCHAR) {\r
6161       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6162       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6163     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6164       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6165       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6166     }\r
6167 \r
6168     if (appData.icsActive) {\r
6169       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6170     }\r
6171     else if (appData.noChessProgram) {\r
6172       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6173     }\r
6174     else {\r
6175       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6176     }\r
6177 \r
6178     SetStartupDialogEnables(hDlg);\r
6179     return TRUE;\r
6180 \r
6181   case WM_COMMAND:\r
6182     switch (LOWORD(wParam)) {\r
6183     case IDOK:\r
6184       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6185         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6186         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6187         p = buf;\r
6188         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6189         ParseArgs(StringGet, &p);\r
6190         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6191         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6192         p = buf;\r
6193         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6194         ParseArgs(StringGet, &p);\r
6195         SwapEngines(singleList); // ... and then make it 'second'\r
6196         appData.noChessProgram = FALSE;\r
6197         appData.icsActive = FALSE;\r
6198       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6199         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6200         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6201         p = buf;\r
6202         ParseArgs(StringGet, &p);\r
6203         if (appData.zippyPlay) {\r
6204           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6205           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6206           p = buf;\r
6207           ParseArgs(StringGet, &p);\r
6208         }\r
6209       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6210         appData.noChessProgram = TRUE;\r
6211         appData.icsActive = FALSE;\r
6212       } else {\r
6213         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6214                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6215         return TRUE;\r
6216       }\r
6217       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6218         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6219         p = buf;\r
6220         ParseArgs(StringGet, &p);\r
6221       }\r
6222       EndDialog(hDlg, TRUE);\r
6223       return TRUE;\r
6224 \r
6225     case IDCANCEL:\r
6226       ExitEvent(0);\r
6227       return TRUE;\r
6228 \r
6229     case IDM_HELPCONTENTS:\r
6230       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6231         MessageBox (GetFocus(),\r
6232                     _("Unable to activate help"),\r
6233                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6234       }\r
6235       break;\r
6236 \r
6237     default:\r
6238       SetStartupDialogEnables(hDlg);\r
6239       break;\r
6240     }\r
6241     break;\r
6242   }\r
6243   return FALSE;\r
6244 }\r
6245 \r
6246 /*---------------------------------------------------------------------------*\\r
6247  *\r
6248  * About box dialog functions\r
6249  *\r
6250 \*---------------------------------------------------------------------------*/\r
6251 \r
6252 /* Process messages for "About" dialog box */\r
6253 LRESULT CALLBACK\r
6254 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6255 {\r
6256   switch (message) {\r
6257   case WM_INITDIALOG: /* message: initialize dialog box */\r
6258     /* Center the dialog over the application window */\r
6259     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6260     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6261     Translate(hDlg, ABOUTBOX);\r
6262     JAWS_COPYRIGHT\r
6263     return (TRUE);\r
6264 \r
6265   case WM_COMMAND: /* message: received a command */\r
6266     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6267         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6268       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6269       return (TRUE);\r
6270     }\r
6271     break;\r
6272   }\r
6273   return (FALSE);\r
6274 }\r
6275 \r
6276 /*---------------------------------------------------------------------------*\\r
6277  *\r
6278  * Comment Dialog functions\r
6279  *\r
6280 \*---------------------------------------------------------------------------*/\r
6281 \r
6282 LRESULT CALLBACK\r
6283 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6284 {\r
6285   static HANDLE hwndText = NULL;\r
6286   int len, newSizeX, newSizeY, flags;\r
6287   static int sizeX, sizeY;\r
6288   char *str;\r
6289   RECT rect;\r
6290   MINMAXINFO *mmi;\r
6291 \r
6292   switch (message) {\r
6293   case WM_INITDIALOG: /* message: initialize dialog box */\r
6294     /* Initialize the dialog items */\r
6295     Translate(hDlg, DLG_EditComment);\r
6296     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6297     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6298     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6299     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6300     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6301     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6302     SetWindowText(hDlg, commentTitle);\r
6303     if (editComment) {\r
6304       SetFocus(hwndText);\r
6305     } else {\r
6306       SetFocus(GetDlgItem(hDlg, IDOK));\r
6307     }\r
6308     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6309                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6310                 MAKELPARAM(FALSE, 0));\r
6311     /* Size and position the dialog */\r
6312     if (!commentDialog) {\r
6313       commentDialog = hDlg;\r
6314       flags = SWP_NOZORDER;\r
6315       GetClientRect(hDlg, &rect);\r
6316       sizeX = rect.right;\r
6317       sizeY = rect.bottom;\r
6318       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6319           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6320         WINDOWPLACEMENT wp;\r
6321         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6322         wp.length = sizeof(WINDOWPLACEMENT);\r
6323         wp.flags = 0;\r
6324         wp.showCmd = SW_SHOW;\r
6325         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6326         wp.rcNormalPosition.left = wpComment.x;\r
6327         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6328         wp.rcNormalPosition.top = wpComment.y;\r
6329         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6330         SetWindowPlacement(hDlg, &wp);\r
6331 \r
6332         GetClientRect(hDlg, &rect);\r
6333         newSizeX = rect.right;\r
6334         newSizeY = rect.bottom;\r
6335         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6336                               newSizeX, newSizeY);\r
6337         sizeX = newSizeX;\r
6338         sizeY = newSizeY;\r
6339       }\r
6340     }\r
6341     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6342     return FALSE;\r
6343 \r
6344   case WM_COMMAND: /* message: received a command */\r
6345     switch (LOWORD(wParam)) {\r
6346     case IDOK:\r
6347       if (editComment) {\r
6348         char *p, *q;\r
6349         /* Read changed options from the dialog box */\r
6350         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6351         len = GetWindowTextLength(hwndText);\r
6352         str = (char *) malloc(len + 1);\r
6353         GetWindowText(hwndText, str, len + 1);\r
6354         p = q = str;\r
6355         while (*q) {\r
6356           if (*q == '\r')\r
6357             q++;\r
6358           else\r
6359             *p++ = *q++;\r
6360         }\r
6361         *p = NULLCHAR;\r
6362         ReplaceComment(commentIndex, str);\r
6363         free(str);\r
6364       }\r
6365       CommentPopDown();\r
6366       return TRUE;\r
6367 \r
6368     case IDCANCEL:\r
6369     case OPT_CancelComment:\r
6370       CommentPopDown();\r
6371       return TRUE;\r
6372 \r
6373     case OPT_ClearComment:\r
6374       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6375       break;\r
6376 \r
6377     case OPT_EditComment:\r
6378       EditCommentEvent();\r
6379       return TRUE;\r
6380 \r
6381     default:\r
6382       break;\r
6383     }\r
6384     break;\r
6385 \r
6386   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6387         if( wParam == OPT_CommentText ) {\r
6388             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6389 \r
6390             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6391                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6392                 POINTL pt;\r
6393                 LRESULT index;\r
6394 \r
6395                 pt.x = LOWORD( lpMF->lParam );\r
6396                 pt.y = HIWORD( lpMF->lParam );\r
6397 \r
6398                 if(lpMF->msg == WM_CHAR) {\r
6399                         CHARRANGE sel;\r
6400                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6401                         index = sel.cpMin;\r
6402                 } else\r
6403                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6404 \r
6405                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6406                 len = GetWindowTextLength(hwndText);\r
6407                 str = (char *) malloc(len + 1);\r
6408                 GetWindowText(hwndText, str, len + 1);\r
6409                 ReplaceComment(commentIndex, str);\r
6410                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6411                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6412                 free(str);\r
6413 \r
6414                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6415                 lpMF->msg = WM_USER;\r
6416 \r
6417                 return TRUE;\r
6418             }\r
6419         }\r
6420         break;\r
6421 \r
6422   case WM_SIZE:\r
6423     newSizeX = LOWORD(lParam);\r
6424     newSizeY = HIWORD(lParam);\r
6425     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6426     sizeX = newSizeX;\r
6427     sizeY = newSizeY;\r
6428     break;\r
6429 \r
6430   case WM_GETMINMAXINFO:\r
6431     /* Prevent resizing window too small */\r
6432     mmi = (MINMAXINFO *) lParam;\r
6433     mmi->ptMinTrackSize.x = 100;\r
6434     mmi->ptMinTrackSize.y = 100;\r
6435     break;\r
6436   }\r
6437   return FALSE;\r
6438 }\r
6439 \r
6440 VOID\r
6441 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6442 {\r
6443   FARPROC lpProc;\r
6444   char *p, *q;\r
6445 \r
6446   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6447 \r
6448   if (str == NULL) str = "";\r
6449   p = (char *) malloc(2 * strlen(str) + 2);\r
6450   q = p;\r
6451   while (*str) {\r
6452     if (*str == '\n') *q++ = '\r';\r
6453     *q++ = *str++;\r
6454   }\r
6455   *q = NULLCHAR;\r
6456   if (commentText != NULL) free(commentText);\r
6457 \r
6458   commentIndex = index;\r
6459   commentTitle = title;\r
6460   commentText = p;\r
6461   editComment = edit;\r
6462 \r
6463   if (commentDialog) {\r
6464     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6465     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6466   } else {\r
6467     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6468     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6469                  hwndMain, (DLGPROC)lpProc);\r
6470     FreeProcInstance(lpProc);\r
6471   }\r
6472   commentUp = TRUE;\r
6473 }\r
6474 \r
6475 \r
6476 /*---------------------------------------------------------------------------*\\r
6477  *\r
6478  * Type-in move dialog functions\r
6479  * \r
6480 \*---------------------------------------------------------------------------*/\r
6481 \r
6482 LRESULT CALLBACK\r
6483 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6484 {\r
6485   char move[MSG_SIZ];\r
6486   HWND hInput;\r
6487 \r
6488   switch (message) {\r
6489   case WM_INITDIALOG:\r
6490     move[0] = (char) lParam;\r
6491     move[1] = NULLCHAR;\r
6492     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6493     Translate(hDlg, DLG_TypeInMove);\r
6494     hInput = GetDlgItem(hDlg, OPT_Move);\r
6495     SetWindowText(hInput, move);\r
6496     SetFocus(hInput);\r
6497     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6498     return FALSE;\r
6499 \r
6500   case WM_COMMAND:\r
6501     switch (LOWORD(wParam)) {\r
6502     case IDOK:\r
6503 \r
6504       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6505       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6506       TypeInDoneEvent(move);\r
6507       EndDialog(hDlg, TRUE);\r
6508       return TRUE;\r
6509     case IDCANCEL:\r
6510       EndDialog(hDlg, FALSE);\r
6511       return TRUE;\r
6512     default:\r
6513       break;\r
6514     }\r
6515     break;\r
6516   }\r
6517   return FALSE;\r
6518 }\r
6519 \r
6520 VOID\r
6521 PopUpMoveDialog(char firstchar)\r
6522 {\r
6523     FARPROC lpProc;\r
6524 \r
6525       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6526       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6527         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6528       FreeProcInstance(lpProc);\r
6529 }\r
6530 \r
6531 /*---------------------------------------------------------------------------*\\r
6532  *\r
6533  * Type-in name dialog functions\r
6534  * \r
6535 \*---------------------------------------------------------------------------*/\r
6536 \r
6537 LRESULT CALLBACK\r
6538 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6539 {\r
6540   char move[MSG_SIZ];\r
6541   HWND hInput;\r
6542 \r
6543   switch (message) {\r
6544   case WM_INITDIALOG:\r
6545     move[0] = (char) lParam;\r
6546     move[1] = NULLCHAR;\r
6547     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6548     Translate(hDlg, DLG_TypeInName);\r
6549     hInput = GetDlgItem(hDlg, OPT_Name);\r
6550     SetWindowText(hInput, move);\r
6551     SetFocus(hInput);\r
6552     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6553     return FALSE;\r
6554 \r
6555   case WM_COMMAND:\r
6556     switch (LOWORD(wParam)) {\r
6557     case IDOK:\r
6558       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6559       appData.userName = strdup(move);\r
6560       SetUserLogo();\r
6561       SetGameInfo();\r
6562       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6563         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6564         DisplayTitle(move);\r
6565       }\r
6566 \r
6567 \r
6568       EndDialog(hDlg, TRUE);\r
6569       return TRUE;\r
6570     case IDCANCEL:\r
6571       EndDialog(hDlg, FALSE);\r
6572       return TRUE;\r
6573     default:\r
6574       break;\r
6575     }\r
6576     break;\r
6577   }\r
6578   return FALSE;\r
6579 }\r
6580 \r
6581 VOID\r
6582 PopUpNameDialog(char firstchar)\r
6583 {\r
6584     FARPROC lpProc;\r
6585     \r
6586       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6587       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6588         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6589       FreeProcInstance(lpProc);\r
6590 }\r
6591 \r
6592 /*---------------------------------------------------------------------------*\\r
6593  *\r
6594  *  Error dialogs\r
6595  * \r
6596 \*---------------------------------------------------------------------------*/\r
6597 \r
6598 /* Nonmodal error box */\r
6599 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6600                              WPARAM wParam, LPARAM lParam);\r
6601 \r
6602 VOID\r
6603 ErrorPopUp(char *title, char *content)\r
6604 {\r
6605   FARPROC lpProc;\r
6606   char *p, *q;\r
6607   BOOLEAN modal = hwndMain == NULL;\r
6608 \r
6609   p = content;\r
6610   q = errorMessage;\r
6611   while (*p) {\r
6612     if (*p == '\n') {\r
6613       if (modal) {\r
6614         *q++ = ' ';\r
6615         p++;\r
6616       } else {\r
6617         *q++ = '\r';\r
6618         *q++ = *p++;\r
6619       }\r
6620     } else {\r
6621       *q++ = *p++;\r
6622     }\r
6623   }\r
6624   *q = NULLCHAR;\r
6625   strncpy(errorTitle, title, sizeof(errorTitle));\r
6626   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6627   \r
6628   if (modal) {\r
6629     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6630   } else {\r
6631     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6632     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6633                  hwndMain, (DLGPROC)lpProc);\r
6634     FreeProcInstance(lpProc);\r
6635   }\r
6636 }\r
6637 \r
6638 VOID\r
6639 ErrorPopDown()\r
6640 {\r
6641   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6642   if (errorDialog == NULL) return;\r
6643   DestroyWindow(errorDialog);\r
6644   errorDialog = NULL;\r
6645   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6646 }\r
6647 \r
6648 LRESULT CALLBACK\r
6649 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6650 {\r
6651   HANDLE hwndText;\r
6652   RECT rChild;\r
6653 \r
6654   switch (message) {\r
6655   case WM_INITDIALOG:\r
6656     GetWindowRect(hDlg, &rChild);\r
6657 \r
6658     /*\r
6659     SetWindowPos(hDlg, NULL, rChild.left,\r
6660       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6661       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6662     */\r
6663 \r
6664     /* \r
6665         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6666         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6667         and it doesn't work when you resize the dialog.\r
6668         For now, just give it a default position.\r
6669     */\r
6670     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6671     Translate(hDlg, DLG_Error);\r
6672 \r
6673     errorDialog = hDlg;\r
6674     SetWindowText(hDlg, errorTitle);\r
6675     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6676     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6677     return FALSE;\r
6678 \r
6679   case WM_COMMAND:\r
6680     switch (LOWORD(wParam)) {\r
6681     case IDOK:\r
6682     case IDCANCEL:\r
6683       if (errorDialog == hDlg) errorDialog = NULL;\r
6684       DestroyWindow(hDlg);\r
6685       return TRUE;\r
6686 \r
6687     default:\r
6688       break;\r
6689     }\r
6690     break;\r
6691   }\r
6692   return FALSE;\r
6693 }\r
6694 \r
6695 #ifdef GOTHIC\r
6696 HWND gothicDialog = NULL;\r
6697 \r
6698 LRESULT CALLBACK\r
6699 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6700 {\r
6701   HANDLE hwndText;\r
6702   RECT rChild;\r
6703   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6704 \r
6705   switch (message) {\r
6706   case WM_INITDIALOG:\r
6707     GetWindowRect(hDlg, &rChild);\r
6708 \r
6709     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6710                                                              SWP_NOZORDER);\r
6711 \r
6712     /* \r
6713         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6714         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6715         and it doesn't work when you resize the dialog.\r
6716         For now, just give it a default position.\r
6717     */\r
6718     gothicDialog = hDlg;\r
6719     SetWindowText(hDlg, errorTitle);\r
6720     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6721     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6722     return FALSE;\r
6723 \r
6724   case WM_COMMAND:\r
6725     switch (LOWORD(wParam)) {\r
6726     case IDOK:\r
6727     case IDCANCEL:\r
6728       if (errorDialog == hDlg) errorDialog = NULL;\r
6729       DestroyWindow(hDlg);\r
6730       return TRUE;\r
6731 \r
6732     default:\r
6733       break;\r
6734     }\r
6735     break;\r
6736   }\r
6737   return FALSE;\r
6738 }\r
6739 \r
6740 VOID\r
6741 GothicPopUp(char *title, VariantClass variant)\r
6742 {\r
6743   FARPROC lpProc;\r
6744   static char *lastTitle;\r
6745 \r
6746   strncpy(errorTitle, title, sizeof(errorTitle));\r
6747   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6748 \r
6749   if(lastTitle != title && gothicDialog != NULL) {\r
6750     DestroyWindow(gothicDialog);\r
6751     gothicDialog = NULL;\r
6752   }\r
6753   if(variant != VariantNormal && gothicDialog == NULL) {\r
6754     title = lastTitle;\r
6755     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6756     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6757                  hwndMain, (DLGPROC)lpProc);\r
6758     FreeProcInstance(lpProc);\r
6759   }\r
6760 }\r
6761 #endif\r
6762 \r
6763 /*---------------------------------------------------------------------------*\\r
6764  *\r
6765  *  Ics Interaction console functions\r
6766  *\r
6767 \*---------------------------------------------------------------------------*/\r
6768 \r
6769 #define HISTORY_SIZE 64\r
6770 static char *history[HISTORY_SIZE];\r
6771 int histIn = 0, histP = 0;\r
6772 \r
6773 VOID\r
6774 SaveInHistory(char *cmd)\r
6775 {\r
6776   if (history[histIn] != NULL) {\r
6777     free(history[histIn]);\r
6778     history[histIn] = NULL;\r
6779   }\r
6780   if (*cmd == NULLCHAR) return;\r
6781   history[histIn] = StrSave(cmd);\r
6782   histIn = (histIn + 1) % HISTORY_SIZE;\r
6783   if (history[histIn] != NULL) {\r
6784     free(history[histIn]);\r
6785     history[histIn] = NULL;\r
6786   }\r
6787   histP = histIn;\r
6788 }\r
6789 \r
6790 char *\r
6791 PrevInHistory(char *cmd)\r
6792 {\r
6793   int newhp;\r
6794   if (histP == histIn) {\r
6795     if (history[histIn] != NULL) free(history[histIn]);\r
6796     history[histIn] = StrSave(cmd);\r
6797   }\r
6798   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6799   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6800   histP = newhp;\r
6801   return history[histP];\r
6802 }\r
6803 \r
6804 char *\r
6805 NextInHistory()\r
6806 {\r
6807   if (histP == histIn) return NULL;\r
6808   histP = (histP + 1) % HISTORY_SIZE;\r
6809   return history[histP];   \r
6810 }\r
6811 \r
6812 HMENU\r
6813 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6814 {\r
6815   HMENU hmenu, h;\r
6816   int i = 0;\r
6817   hmenu = LoadMenu(hInst, "TextMenu");\r
6818   h = GetSubMenu(hmenu, 0);\r
6819   while (e->item) {\r
6820     if (strcmp(e->item, "-") == 0) {\r
6821       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6822     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6823       int flags = MF_STRING, j = 0;\r
6824       if (e->item[0] == '|') {\r
6825         flags |= MF_MENUBARBREAK;\r
6826         j++;\r
6827       }\r
6828       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6829       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6830     }\r
6831     e++;\r
6832     i++;\r
6833   } \r
6834   return hmenu;\r
6835 }\r
6836 \r
6837 WNDPROC consoleTextWindowProc;\r
6838 \r
6839 void\r
6840 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6841 {\r
6842   char buf[MSG_SIZ], name[MSG_SIZ];\r
6843   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6844   CHARRANGE sel;\r
6845 \r
6846   if (!getname) {\r
6847     SetWindowText(hInput, command);\r
6848     if (immediate) {\r
6849       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6850     } else {\r
6851       sel.cpMin = 999999;\r
6852       sel.cpMax = 999999;\r
6853       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6854       SetFocus(hInput);\r
6855     }\r
6856     return;\r
6857   }    \r
6858   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6859   if (sel.cpMin == sel.cpMax) {\r
6860     /* Expand to surrounding word */\r
6861     TEXTRANGE tr;\r
6862     do {\r
6863       tr.chrg.cpMax = sel.cpMin;\r
6864       tr.chrg.cpMin = --sel.cpMin;\r
6865       if (sel.cpMin < 0) break;\r
6866       tr.lpstrText = name;\r
6867       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6868     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6869     sel.cpMin++;\r
6870 \r
6871     do {\r
6872       tr.chrg.cpMin = sel.cpMax;\r
6873       tr.chrg.cpMax = ++sel.cpMax;\r
6874       tr.lpstrText = name;\r
6875       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6876     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6877     sel.cpMax--;\r
6878 \r
6879     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6880       MessageBeep(MB_ICONEXCLAMATION);\r
6881       return;\r
6882     }\r
6883     tr.chrg = sel;\r
6884     tr.lpstrText = name;\r
6885     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6886   } else {\r
6887     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6888       MessageBeep(MB_ICONEXCLAMATION);\r
6889       return;\r
6890     }\r
6891     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6892   }\r
6893   if (immediate) {\r
6894     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6895     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6896     SetWindowText(hInput, buf);\r
6897     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6898   } else {\r
6899     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6900       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6901     SetWindowText(hInput, buf);\r
6902     sel.cpMin = 999999;\r
6903     sel.cpMax = 999999;\r
6904     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6905     SetFocus(hInput);\r
6906   }\r
6907 }\r
6908 \r
6909 LRESULT CALLBACK \r
6910 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6911 {\r
6912   HWND hInput;\r
6913   CHARRANGE sel;\r
6914 \r
6915   switch (message) {\r
6916   case WM_KEYDOWN:\r
6917     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6918     if(wParam=='R') return 0;\r
6919     switch (wParam) {\r
6920     case VK_PRIOR:\r
6921       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6922       return 0;\r
6923     case VK_NEXT:\r
6924       sel.cpMin = 999999;\r
6925       sel.cpMax = 999999;\r
6926       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6927       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6928       return 0;\r
6929     }\r
6930     break;\r
6931   case WM_CHAR:\r
6932    if(wParam != '\022') {\r
6933     if (wParam == '\t') {\r
6934       if (GetKeyState(VK_SHIFT) < 0) {\r
6935         /* shifted */\r
6936         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6937         if (buttonDesc[0].hwnd) {\r
6938           SetFocus(buttonDesc[0].hwnd);\r
6939         } else {\r
6940           SetFocus(hwndMain);\r
6941         }\r
6942       } else {\r
6943         /* unshifted */\r
6944         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6945       }\r
6946     } else {\r
6947       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6948       JAWS_DELETE( SetFocus(hInput); )\r
6949       SendMessage(hInput, message, wParam, lParam);\r
6950     }\r
6951     return 0;\r
6952    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6953    lParam = -1;\r
6954   case WM_RBUTTONDOWN:\r
6955     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6956       /* Move selection here if it was empty */\r
6957       POINT pt;\r
6958       pt.x = LOWORD(lParam);\r
6959       pt.y = HIWORD(lParam);\r
6960       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6961       if (sel.cpMin == sel.cpMax) {\r
6962         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6963         sel.cpMax = sel.cpMin;\r
6964         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6965       }\r
6966       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6967 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6968       POINT pt;\r
6969       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6970       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6971       if (sel.cpMin == sel.cpMax) {\r
6972         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6973         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6974       }\r
6975       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6976         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6977       }\r
6978       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6979       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6980       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6981       MenuPopup(hwnd, pt, hmenu, -1);\r
6982 }\r
6983     }\r
6984     return 0;\r
6985   case WM_RBUTTONUP:\r
6986     if (GetKeyState(VK_SHIFT) & ~1) {\r
6987       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6988         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6989     }\r
6990     return 0;\r
6991   case WM_PASTE:\r
6992     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6993     SetFocus(hInput);\r
6994     return SendMessage(hInput, message, wParam, lParam);\r
6995   case WM_MBUTTONDOWN:\r
6996     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6997   case WM_COMMAND:\r
6998     switch (LOWORD(wParam)) {\r
6999     case IDM_QuickPaste:\r
7000       {\r
7001         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7002         if (sel.cpMin == sel.cpMax) {\r
7003           MessageBeep(MB_ICONEXCLAMATION);\r
7004           return 0;\r
7005         }\r
7006         SendMessage(hwnd, WM_COPY, 0, 0);\r
7007         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7008         SendMessage(hInput, WM_PASTE, 0, 0);\r
7009         SetFocus(hInput);\r
7010         return 0;\r
7011       }\r
7012     case IDM_Cut:\r
7013       SendMessage(hwnd, WM_CUT, 0, 0);\r
7014       return 0;\r
7015     case IDM_Paste:\r
7016       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7017       return 0;\r
7018     case IDM_Copy:\r
7019       SendMessage(hwnd, WM_COPY, 0, 0);\r
7020       return 0;\r
7021     default:\r
7022       {\r
7023         int i = LOWORD(wParam) - IDM_CommandX;\r
7024         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7025             icsTextMenuEntry[i].command != NULL) {\r
7026           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7027                    icsTextMenuEntry[i].getname,\r
7028                    icsTextMenuEntry[i].immediate);\r
7029           return 0;\r
7030         }\r
7031       }\r
7032       break;\r
7033     }\r
7034     break;\r
7035   }\r
7036   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7037 }\r
7038 \r
7039 WNDPROC consoleInputWindowProc;\r
7040 \r
7041 LRESULT CALLBACK\r
7042 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7043 {\r
7044   char buf[MSG_SIZ];\r
7045   char *p;\r
7046   static BOOL sendNextChar = FALSE;\r
7047   static BOOL quoteNextChar = FALSE;\r
7048   InputSource *is = consoleInputSource;\r
7049   CHARFORMAT cf;\r
7050   CHARRANGE sel;\r
7051 \r
7052   switch (message) {\r
7053   case WM_CHAR:\r
7054     if (!appData.localLineEditing || sendNextChar) {\r
7055       is->buf[0] = (CHAR) wParam;\r
7056       is->count = 1;\r
7057       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7058       sendNextChar = FALSE;\r
7059       return 0;\r
7060     }\r
7061     if (quoteNextChar) {\r
7062       buf[0] = (char) wParam;\r
7063       buf[1] = NULLCHAR;\r
7064       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7065       quoteNextChar = FALSE;\r
7066       return 0;\r
7067     }\r
7068     switch (wParam) {\r
7069     case '\r':   /* Enter key */\r
7070       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7071       if (consoleEcho) SaveInHistory(is->buf);\r
7072       is->buf[is->count++] = '\n';\r
7073       is->buf[is->count] = NULLCHAR;\r
7074       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7075       if (consoleEcho) {\r
7076         ConsoleOutput(is->buf, is->count, TRUE);\r
7077       } else if (appData.localLineEditing) {\r
7078         ConsoleOutput("\n", 1, TRUE);\r
7079       }\r
7080       /* fall thru */\r
7081     case '\033': /* Escape key */\r
7082       SetWindowText(hwnd, "");\r
7083       cf.cbSize = sizeof(CHARFORMAT);\r
7084       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7085       if (consoleEcho) {\r
7086         cf.crTextColor = textAttribs[ColorNormal].color;\r
7087       } else {\r
7088         cf.crTextColor = COLOR_ECHOOFF;\r
7089       }\r
7090       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7091       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7092       return 0;\r
7093     case '\t':   /* Tab key */\r
7094       if (GetKeyState(VK_SHIFT) < 0) {\r
7095         /* shifted */\r
7096         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7097       } else {\r
7098         /* unshifted */\r
7099         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7100         if (buttonDesc[0].hwnd) {\r
7101           SetFocus(buttonDesc[0].hwnd);\r
7102         } else {\r
7103           SetFocus(hwndMain);\r
7104         }\r
7105       }\r
7106       return 0;\r
7107     case '\023': /* Ctrl+S */\r
7108       sendNextChar = TRUE;\r
7109       return 0;\r
7110     case '\021': /* Ctrl+Q */\r
7111       quoteNextChar = TRUE;\r
7112       return 0;\r
7113     JAWS_REPLAY\r
7114     default:\r
7115       break;\r
7116     }\r
7117     break;\r
7118   case WM_KEYDOWN:\r
7119     switch (wParam) {\r
7120     case VK_UP:\r
7121       GetWindowText(hwnd, buf, MSG_SIZ);\r
7122       p = PrevInHistory(buf);\r
7123       if (p != NULL) {\r
7124         SetWindowText(hwnd, p);\r
7125         sel.cpMin = 999999;\r
7126         sel.cpMax = 999999;\r
7127         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7128         return 0;\r
7129       }\r
7130       break;\r
7131     case VK_DOWN:\r
7132       p = NextInHistory();\r
7133       if (p != NULL) {\r
7134         SetWindowText(hwnd, p);\r
7135         sel.cpMin = 999999;\r
7136         sel.cpMax = 999999;\r
7137         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7138         return 0;\r
7139       }\r
7140       break;\r
7141     case VK_HOME:\r
7142     case VK_END:\r
7143       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7144       /* fall thru */\r
7145     case VK_PRIOR:\r
7146     case VK_NEXT:\r
7147       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7148       return 0;\r
7149     }\r
7150     break;\r
7151   case WM_MBUTTONDOWN:\r
7152     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7153       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7154     break;\r
7155   case WM_RBUTTONUP:\r
7156     if (GetKeyState(VK_SHIFT) & ~1) {\r
7157       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7158         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7159     } else {\r
7160       POINT pt;\r
7161       HMENU hmenu;\r
7162       hmenu = LoadMenu(hInst, "InputMenu");\r
7163       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7164       if (sel.cpMin == sel.cpMax) {\r
7165         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7166         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7167       }\r
7168       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7169         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7170       }\r
7171       pt.x = LOWORD(lParam);\r
7172       pt.y = HIWORD(lParam);\r
7173       MenuPopup(hwnd, pt, hmenu, -1);\r
7174     }\r
7175     return 0;\r
7176   case WM_COMMAND:\r
7177     switch (LOWORD(wParam)) { \r
7178     case IDM_Undo:\r
7179       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7180       return 0;\r
7181     case IDM_SelectAll:\r
7182       sel.cpMin = 0;\r
7183       sel.cpMax = -1; /*999999?*/\r
7184       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7185       return 0;\r
7186     case IDM_Cut:\r
7187       SendMessage(hwnd, WM_CUT, 0, 0);\r
7188       return 0;\r
7189     case IDM_Paste:\r
7190       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7191       return 0;\r
7192     case IDM_Copy:\r
7193       SendMessage(hwnd, WM_COPY, 0, 0);\r
7194       return 0;\r
7195     }\r
7196     break;\r
7197   }\r
7198   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7199 }\r
7200 \r
7201 #define CO_MAX  100000\r
7202 #define CO_TRIM   1000\r
7203 \r
7204 LRESULT CALLBACK\r
7205 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7206 {\r
7207   static SnapData sd;\r
7208   HWND hText, hInput;\r
7209   RECT rect;\r
7210   static int sizeX, sizeY;\r
7211   int newSizeX, newSizeY;\r
7212   MINMAXINFO *mmi;\r
7213   WORD wMask;\r
7214 \r
7215   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7216   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7217 \r
7218   switch (message) {\r
7219   case WM_NOTIFY:\r
7220     if (((NMHDR*)lParam)->code == EN_LINK)\r
7221     {\r
7222       ENLINK *pLink = (ENLINK*)lParam;\r
7223       if (pLink->msg == WM_LBUTTONUP)\r
7224       {\r
7225         TEXTRANGE tr;\r
7226 \r
7227         tr.chrg = pLink->chrg;\r
7228         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7229         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7230         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7231         free(tr.lpstrText);\r
7232       }\r
7233     }\r
7234     break;\r
7235   case WM_INITDIALOG: /* message: initialize dialog box */\r
7236     hwndConsole = hDlg;\r
7237     SetFocus(hInput);\r
7238     consoleTextWindowProc = (WNDPROC)\r
7239       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7240     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7241     consoleInputWindowProc = (WNDPROC)\r
7242       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7243     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7244     Colorize(ColorNormal, TRUE);\r
7245     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7246     ChangedConsoleFont();\r
7247     GetClientRect(hDlg, &rect);\r
7248     sizeX = rect.right;\r
7249     sizeY = rect.bottom;\r
7250     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7251         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7252       WINDOWPLACEMENT wp;\r
7253       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7254       wp.length = sizeof(WINDOWPLACEMENT);\r
7255       wp.flags = 0;\r
7256       wp.showCmd = SW_SHOW;\r
7257       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7258       wp.rcNormalPosition.left = wpConsole.x;\r
7259       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7260       wp.rcNormalPosition.top = wpConsole.y;\r
7261       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7262       SetWindowPlacement(hDlg, &wp);\r
7263     }\r
7264 \r
7265    // [HGM] Chessknight's change 2004-07-13\r
7266    else { /* Determine Defaults */\r
7267        WINDOWPLACEMENT wp;\r
7268        wpConsole.x = wpMain.width + 1;\r
7269        wpConsole.y = wpMain.y;\r
7270        wpConsole.width = screenWidth -  wpMain.width;\r
7271        wpConsole.height = wpMain.height;\r
7272        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7273        wp.length = sizeof(WINDOWPLACEMENT);\r
7274        wp.flags = 0;\r
7275        wp.showCmd = SW_SHOW;\r
7276        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7277        wp.rcNormalPosition.left = wpConsole.x;\r
7278        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7279        wp.rcNormalPosition.top = wpConsole.y;\r
7280        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7281        SetWindowPlacement(hDlg, &wp);\r
7282     }\r
7283 \r
7284    // Allow hText to highlight URLs and send notifications on them\r
7285    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7286    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7287    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7288    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7289 \r
7290     return FALSE;\r
7291 \r
7292   case WM_SETFOCUS:\r
7293     SetFocus(hInput);\r
7294     return 0;\r
7295 \r
7296   case WM_CLOSE:\r
7297     ExitEvent(0);\r
7298     /* not reached */\r
7299     break;\r
7300 \r
7301   case WM_SIZE:\r
7302     if (IsIconic(hDlg)) break;\r
7303     newSizeX = LOWORD(lParam);\r
7304     newSizeY = HIWORD(lParam);\r
7305     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7306       RECT rectText, rectInput;\r
7307       POINT pt;\r
7308       int newTextHeight, newTextWidth;\r
7309       GetWindowRect(hText, &rectText);\r
7310       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7311       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7312       if (newTextHeight < 0) {\r
7313         newSizeY += -newTextHeight;\r
7314         newTextHeight = 0;\r
7315       }\r
7316       SetWindowPos(hText, NULL, 0, 0,\r
7317         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7318       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7319       pt.x = rectInput.left;\r
7320       pt.y = rectInput.top + newSizeY - sizeY;\r
7321       ScreenToClient(hDlg, &pt);\r
7322       SetWindowPos(hInput, NULL, \r
7323         pt.x, pt.y, /* needs client coords */   \r
7324         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7325         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7326     }\r
7327     sizeX = newSizeX;\r
7328     sizeY = newSizeY;\r
7329     break;\r
7330 \r
7331   case WM_GETMINMAXINFO:\r
7332     /* Prevent resizing window too small */\r
7333     mmi = (MINMAXINFO *) lParam;\r
7334     mmi->ptMinTrackSize.x = 100;\r
7335     mmi->ptMinTrackSize.y = 100;\r
7336     break;\r
7337 \r
7338   /* [AS] Snapping */\r
7339   case WM_ENTERSIZEMOVE:\r
7340     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7341 \r
7342   case WM_SIZING:\r
7343     return OnSizing( &sd, hDlg, wParam, lParam );\r
7344 \r
7345   case WM_MOVING:\r
7346     return OnMoving( &sd, hDlg, wParam, lParam );\r
7347 \r
7348   case WM_EXITSIZEMOVE:\r
7349         UpdateICSWidth(hText);\r
7350     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7351   }\r
7352 \r
7353   return DefWindowProc(hDlg, message, wParam, lParam);\r
7354 }\r
7355 \r
7356 \r
7357 VOID\r
7358 ConsoleCreate()\r
7359 {\r
7360   HWND hCons;\r
7361   if (hwndConsole) return;\r
7362   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7363   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7364 }\r
7365 \r
7366 \r
7367 VOID\r
7368 ConsoleOutput(char* data, int length, int forceVisible)\r
7369 {\r
7370   HWND hText;\r
7371   int trim, exlen;\r
7372   char *p, *q;\r
7373   char buf[CO_MAX+1];\r
7374   POINT pEnd;\r
7375   RECT rect;\r
7376   static int delayLF = 0;\r
7377   CHARRANGE savesel, sel;\r
7378 \r
7379   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7380   p = data;\r
7381   q = buf;\r
7382   if (delayLF) {\r
7383     *q++ = '\r';\r
7384     *q++ = '\n';\r
7385     delayLF = 0;\r
7386   }\r
7387   while (length--) {\r
7388     if (*p == '\n') {\r
7389       if (*++p) {\r
7390         *q++ = '\r';\r
7391         *q++ = '\n';\r
7392       } else {\r
7393         delayLF = 1;\r
7394       }\r
7395     } else if (*p == '\007') {\r
7396        MyPlaySound(&sounds[(int)SoundBell]);\r
7397        p++;\r
7398     } else {\r
7399       *q++ = *p++;\r
7400     }\r
7401   }\r
7402   *q = NULLCHAR;\r
7403   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7404   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7405   /* Save current selection */\r
7406   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7407   exlen = GetWindowTextLength(hText);\r
7408   /* Find out whether current end of text is visible */\r
7409   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7410   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7411   /* Trim existing text if it's too long */\r
7412   if (exlen + (q - buf) > CO_MAX) {\r
7413     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7414     sel.cpMin = 0;\r
7415     sel.cpMax = trim;\r
7416     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7417     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7418     exlen -= trim;\r
7419     savesel.cpMin -= trim;\r
7420     savesel.cpMax -= trim;\r
7421     if (exlen < 0) exlen = 0;\r
7422     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7423     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7424   }\r
7425   /* Append the new text */\r
7426   sel.cpMin = exlen;\r
7427   sel.cpMax = exlen;\r
7428   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7429   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7430   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7431   if (forceVisible || exlen == 0 ||\r
7432       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7433        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7434     /* Scroll to make new end of text visible if old end of text\r
7435        was visible or new text is an echo of user typein */\r
7436     sel.cpMin = 9999999;\r
7437     sel.cpMax = 9999999;\r
7438     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7439     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7440     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7441     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7442   }\r
7443   if (savesel.cpMax == exlen || forceVisible) {\r
7444     /* Move insert point to new end of text if it was at the old\r
7445        end of text or if the new text is an echo of user typein */\r
7446     sel.cpMin = 9999999;\r
7447     sel.cpMax = 9999999;\r
7448     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7449   } else {\r
7450     /* Restore previous selection */\r
7451     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7452   }\r
7453   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7454 }\r
7455 \r
7456 /*---------*/\r
7457 \r
7458 \r
7459 void\r
7460 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7461 {\r
7462   char buf[100];\r
7463   char *str;\r
7464   COLORREF oldFg, oldBg;\r
7465   HFONT oldFont;\r
7466   RECT rect;\r
7467 \r
7468   if(copyNumber > 1)\r
7469     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7470 \r
7471   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7472   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7473   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7474 \r
7475   rect.left = x;\r
7476   rect.right = x + squareSize;\r
7477   rect.top  = y;\r
7478   rect.bottom = y + squareSize;\r
7479   str = buf;\r
7480 \r
7481   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7482                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7483              y, ETO_CLIPPED|ETO_OPAQUE,\r
7484              &rect, str, strlen(str), NULL);\r
7485 \r
7486   (void) SetTextColor(hdc, oldFg);\r
7487   (void) SetBkColor(hdc, oldBg);\r
7488   (void) SelectObject(hdc, oldFont);\r
7489 }\r
7490 \r
7491 void\r
7492 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7493               RECT *rect, char *color, char *flagFell)\r
7494 {\r
7495   char buf[100];\r
7496   char *str;\r
7497   COLORREF oldFg, oldBg;\r
7498   HFONT oldFont;\r
7499 \r
7500   if (twoBoards && partnerUp) return;\r
7501   if (appData.clockMode) {\r
7502     if (tinyLayout)\r
7503       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7504     else\r
7505       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7506     str = buf;\r
7507   } else {\r
7508     str = color;\r
7509   }\r
7510 \r
7511   if (highlight) {\r
7512     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7513     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7514   } else {\r
7515     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7516     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7517   }\r
7518   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7519 \r
7520   JAWS_SILENCE\r
7521 \r
7522   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7523              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7524              rect, str, strlen(str), NULL);\r
7525   if(logoHeight > 0 && appData.clockMode) {\r
7526       RECT r;\r
7527       str += strlen(color)+2;\r
7528       r.top = rect->top + logoHeight/2;\r
7529       r.left = rect->left;\r
7530       r.right = rect->right;\r
7531       r.bottom = rect->bottom;\r
7532       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7533                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7534                  &r, str, strlen(str), NULL);\r
7535   }\r
7536   (void) SetTextColor(hdc, oldFg);\r
7537   (void) SetBkColor(hdc, oldBg);\r
7538   (void) SelectObject(hdc, oldFont);\r
7539 }\r
7540 \r
7541 \r
7542 int\r
7543 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7544            OVERLAPPED *ovl)\r
7545 {\r
7546   int ok, err;\r
7547 \r
7548   /* [AS]  */\r
7549   if( count <= 0 ) {\r
7550     if (appData.debugMode) {\r
7551       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7552     }\r
7553 \r
7554     return ERROR_INVALID_USER_BUFFER;\r
7555   }\r
7556 \r
7557   ResetEvent(ovl->hEvent);\r
7558   ovl->Offset = ovl->OffsetHigh = 0;\r
7559   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7560   if (ok) {\r
7561     err = NO_ERROR;\r
7562   } else {\r
7563     err = GetLastError();\r
7564     if (err == ERROR_IO_PENDING) {\r
7565       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7566       if (ok)\r
7567         err = NO_ERROR;\r
7568       else\r
7569         err = GetLastError();\r
7570     }\r
7571   }\r
7572   return err;\r
7573 }\r
7574 \r
7575 int\r
7576 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7577             OVERLAPPED *ovl)\r
7578 {\r
7579   int ok, err;\r
7580 \r
7581   ResetEvent(ovl->hEvent);\r
7582   ovl->Offset = ovl->OffsetHigh = 0;\r
7583   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7584   if (ok) {\r
7585     err = NO_ERROR;\r
7586   } else {\r
7587     err = GetLastError();\r
7588     if (err == ERROR_IO_PENDING) {\r
7589       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7590       if (ok)\r
7591         err = NO_ERROR;\r
7592       else\r
7593         err = GetLastError();\r
7594     }\r
7595   }\r
7596   return err;\r
7597 }\r
7598 \r
7599 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7600 void CheckForInputBufferFull( InputSource * is )\r
7601 {\r
7602     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7603         /* Look for end of line */\r
7604         char * p = is->buf;\r
7605         \r
7606         while( p < is->next && *p != '\n' ) {\r
7607             p++;\r
7608         }\r
7609 \r
7610         if( p >= is->next ) {\r
7611             if (appData.debugMode) {\r
7612                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7613             }\r
7614 \r
7615             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7616             is->count = (DWORD) -1;\r
7617             is->next = is->buf;\r
7618         }\r
7619     }\r
7620 }\r
7621 \r
7622 DWORD\r
7623 InputThread(LPVOID arg)\r
7624 {\r
7625   InputSource *is;\r
7626   OVERLAPPED ovl;\r
7627 \r
7628   is = (InputSource *) arg;\r
7629   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7630   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7631   while (is->hThread != NULL) {\r
7632     is->error = DoReadFile(is->hFile, is->next,\r
7633                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7634                            &is->count, &ovl);\r
7635     if (is->error == NO_ERROR) {\r
7636       is->next += is->count;\r
7637     } else {\r
7638       if (is->error == ERROR_BROKEN_PIPE) {\r
7639         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7640         is->count = 0;\r
7641       } else {\r
7642         is->count = (DWORD) -1;\r
7643         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7644         break; \r
7645       }\r
7646     }\r
7647 \r
7648     CheckForInputBufferFull( is );\r
7649 \r
7650     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7651 \r
7652     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7653 \r
7654     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7655   }\r
7656 \r
7657   CloseHandle(ovl.hEvent);\r
7658   CloseHandle(is->hFile);\r
7659 \r
7660   if (appData.debugMode) {\r
7661     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7662   }\r
7663 \r
7664   return 0;\r
7665 }\r
7666 \r
7667 \r
7668 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7669 DWORD\r
7670 NonOvlInputThread(LPVOID arg)\r
7671 {\r
7672   InputSource *is;\r
7673   char *p, *q;\r
7674   int i;\r
7675   char prev;\r
7676 \r
7677   is = (InputSource *) arg;\r
7678   while (is->hThread != NULL) {\r
7679     is->error = ReadFile(is->hFile, is->next,\r
7680                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7681                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7682     if (is->error == NO_ERROR) {\r
7683       /* Change CRLF to LF */\r
7684       if (is->next > is->buf) {\r
7685         p = is->next - 1;\r
7686         i = is->count + 1;\r
7687       } else {\r
7688         p = is->next;\r
7689         i = is->count;\r
7690       }\r
7691       q = p;\r
7692       prev = NULLCHAR;\r
7693       while (i > 0) {\r
7694         if (prev == '\r' && *p == '\n') {\r
7695           *(q-1) = '\n';\r
7696           is->count--;\r
7697         } else { \r
7698           *q++ = *p;\r
7699         }\r
7700         prev = *p++;\r
7701         i--;\r
7702       }\r
7703       *q = NULLCHAR;\r
7704       is->next = q;\r
7705     } else {\r
7706       if (is->error == ERROR_BROKEN_PIPE) {\r
7707         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7708         is->count = 0; \r
7709       } else {\r
7710         is->count = (DWORD) -1;\r
7711       }\r
7712     }\r
7713 \r
7714     CheckForInputBufferFull( is );\r
7715 \r
7716     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7717 \r
7718     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7719 \r
7720     if (is->count < 0) break;  /* Quit on error */\r
7721   }\r
7722   CloseHandle(is->hFile);\r
7723   return 0;\r
7724 }\r
7725 \r
7726 DWORD\r
7727 SocketInputThread(LPVOID arg)\r
7728 {\r
7729   InputSource *is;\r
7730 \r
7731   is = (InputSource *) arg;\r
7732   while (is->hThread != NULL) {\r
7733     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7734     if ((int)is->count == SOCKET_ERROR) {\r
7735       is->count = (DWORD) -1;\r
7736       is->error = WSAGetLastError();\r
7737     } else {\r
7738       is->error = NO_ERROR;\r
7739       is->next += is->count;\r
7740       if (is->count == 0 && is->second == is) {\r
7741         /* End of file on stderr; quit with no message */\r
7742         break;\r
7743       }\r
7744     }\r
7745     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7746 \r
7747     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7748 \r
7749     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7750   }\r
7751   return 0;\r
7752 }\r
7753 \r
7754 VOID\r
7755 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7756 {\r
7757   InputSource *is;\r
7758 \r
7759   is = (InputSource *) lParam;\r
7760   if (is->lineByLine) {\r
7761     /* Feed in lines one by one */\r
7762     char *p = is->buf;\r
7763     char *q = p;\r
7764     while (q < is->next) {\r
7765       if (*q++ == '\n') {\r
7766         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7767         p = q;\r
7768       }\r
7769     }\r
7770     \r
7771     /* Move any partial line to the start of the buffer */\r
7772     q = is->buf;\r
7773     while (p < is->next) {\r
7774       *q++ = *p++;\r
7775     }\r
7776     is->next = q;\r
7777 \r
7778     if (is->error != NO_ERROR || is->count == 0) {\r
7779       /* Notify backend of the error.  Note: If there was a partial\r
7780          line at the end, it is not flushed through. */\r
7781       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7782     }\r
7783   } else {\r
7784     /* Feed in the whole chunk of input at once */\r
7785     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7786     is->next = is->buf;\r
7787   }\r
7788 }\r
7789 \r
7790 /*---------------------------------------------------------------------------*\\r
7791  *\r
7792  *  Menu enables. Used when setting various modes.\r
7793  *\r
7794 \*---------------------------------------------------------------------------*/\r
7795 \r
7796 typedef struct {\r
7797   int item;\r
7798   int flags;\r
7799 } Enables;\r
7800 \r
7801 VOID\r
7802 GreyRevert(Boolean grey)\r
7803 { // [HGM] vari: for retracting variations in local mode\r
7804   HMENU hmenu = GetMenu(hwndMain);\r
7805   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7806   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7807 }\r
7808 \r
7809 VOID\r
7810 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7811 {\r
7812   while (enab->item > 0) {\r
7813     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7814     enab++;\r
7815   }\r
7816 }\r
7817 \r
7818 Enables gnuEnables[] = {\r
7819   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7832 \r
7833   // Needed to switch from ncp to GNU mode on Engine Load\r
7834   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7835   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7836   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7837   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7844   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7848   { -1, -1 }\r
7849 };\r
7850 \r
7851 Enables icsEnables[] = {\r
7852   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7872   { -1, -1 }\r
7873 };\r
7874 \r
7875 #if ZIPPY\r
7876 Enables zippyEnables[] = {\r
7877   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7881   { -1, -1 }\r
7882 };\r
7883 #endif\r
7884 \r
7885 Enables ncpEnables[] = {\r
7886   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7895   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7896   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7908   { -1, -1 }\r
7909 };\r
7910 \r
7911 Enables trainingOnEnables[] = {\r
7912   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7921   { -1, -1 }\r
7922 };\r
7923 \r
7924 Enables trainingOffEnables[] = {\r
7925   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7934   { -1, -1 }\r
7935 };\r
7936 \r
7937 /* These modify either ncpEnables or gnuEnables */\r
7938 Enables cmailEnables[] = {\r
7939   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7940   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7941   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7942   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7946   { -1, -1 }\r
7947 };\r
7948 \r
7949 Enables machineThinkingEnables[] = {\r
7950   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7963 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7966   { -1, -1 }\r
7967 };\r
7968 \r
7969 Enables userThinkingEnables[] = {\r
7970   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7971   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7983 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7986   { -1, -1 }\r
7987 };\r
7988 \r
7989 /*---------------------------------------------------------------------------*\\r
7990  *\r
7991  *  Front-end interface functions exported by XBoard.\r
7992  *  Functions appear in same order as prototypes in frontend.h.\r
7993  * \r
7994 \*---------------------------------------------------------------------------*/\r
7995 VOID\r
7996 CheckMark(UINT item, int state)\r
7997 {\r
7998     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7999 }\r
8000 \r
8001 VOID\r
8002 ModeHighlight()\r
8003 {\r
8004   static UINT prevChecked = 0;\r
8005   static int prevPausing = 0;\r
8006   UINT nowChecked;\r
8007 \r
8008   if (pausing != prevPausing) {\r
8009     prevPausing = pausing;\r
8010     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8011                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8012     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8013   }\r
8014 \r
8015   switch (gameMode) {\r
8016   case BeginningOfGame:\r
8017     if (appData.icsActive)\r
8018       nowChecked = IDM_IcsClient;\r
8019     else if (appData.noChessProgram)\r
8020       nowChecked = IDM_EditGame;\r
8021     else\r
8022       nowChecked = IDM_MachineBlack;\r
8023     break;\r
8024   case MachinePlaysBlack:\r
8025     nowChecked = IDM_MachineBlack;\r
8026     break;\r
8027   case MachinePlaysWhite:\r
8028     nowChecked = IDM_MachineWhite;\r
8029     break;\r
8030   case TwoMachinesPlay:\r
8031     nowChecked = IDM_TwoMachines;\r
8032     break;\r
8033   case AnalyzeMode:\r
8034     nowChecked = IDM_AnalysisMode;\r
8035     break;\r
8036   case AnalyzeFile:\r
8037     nowChecked = IDM_AnalyzeFile;\r
8038     break;\r
8039   case EditGame:\r
8040     nowChecked = IDM_EditGame;\r
8041     break;\r
8042   case PlayFromGameFile:\r
8043     nowChecked = IDM_LoadGame;\r
8044     break;\r
8045   case EditPosition:\r
8046     nowChecked = IDM_EditPosition;\r
8047     break;\r
8048   case Training:\r
8049     nowChecked = IDM_Training;\r
8050     break;\r
8051   case IcsPlayingWhite:\r
8052   case IcsPlayingBlack:\r
8053   case IcsObserving:\r
8054   case IcsIdle:\r
8055     nowChecked = IDM_IcsClient;\r
8056     break;\r
8057   default:\r
8058   case EndOfGame:\r
8059     nowChecked = 0;\r
8060     break;\r
8061   }\r
8062   CheckMark(prevChecked, MF_UNCHECKED);\r
8063   CheckMark(nowChecked, MF_CHECKED);\r
8064   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8065 \r
8066   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8067     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8068                           MF_BYCOMMAND|MF_ENABLED);\r
8069   } else {\r
8070     (void) EnableMenuItem(GetMenu(hwndMain), \r
8071                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8072   }\r
8073 \r
8074   prevChecked = nowChecked;\r
8075 \r
8076   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8077   if (appData.icsActive) {\r
8078        if (appData.icsEngineAnalyze) {\r
8079                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8080        } else {\r
8081                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8082        }\r
8083   }\r
8084   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8085 }\r
8086 \r
8087 VOID\r
8088 SetICSMode()\r
8089 {\r
8090   HMENU hmenu = GetMenu(hwndMain);\r
8091   SetMenuEnables(hmenu, icsEnables);\r
8092   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8093     MF_BYCOMMAND|MF_ENABLED);\r
8094 #if ZIPPY\r
8095   if (appData.zippyPlay) {\r
8096     SetMenuEnables(hmenu, zippyEnables);\r
8097     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8098          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8099           MF_BYCOMMAND|MF_ENABLED);\r
8100   }\r
8101 #endif\r
8102 }\r
8103 \r
8104 VOID\r
8105 SetGNUMode()\r
8106 {\r
8107   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8108 }\r
8109 \r
8110 VOID\r
8111 SetNCPMode()\r
8112 {\r
8113   HMENU hmenu = GetMenu(hwndMain);\r
8114   SetMenuEnables(hmenu, ncpEnables);\r
8115     DrawMenuBar(hwndMain);\r
8116 }\r
8117 \r
8118 VOID\r
8119 SetCmailMode()\r
8120 {\r
8121   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8122 }\r
8123 \r
8124 VOID \r
8125 SetTrainingModeOn()\r
8126 {\r
8127   int i;\r
8128   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8129   for (i = 0; i < N_BUTTONS; i++) {\r
8130     if (buttonDesc[i].hwnd != NULL)\r
8131       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8132   }\r
8133   CommentPopDown();\r
8134 }\r
8135 \r
8136 VOID SetTrainingModeOff()\r
8137 {\r
8138   int i;\r
8139   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8140   for (i = 0; i < N_BUTTONS; i++) {\r
8141     if (buttonDesc[i].hwnd != NULL)\r
8142       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8143   }\r
8144 }\r
8145 \r
8146 \r
8147 VOID\r
8148 SetUserThinkingEnables()\r
8149 {\r
8150   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8151 }\r
8152 \r
8153 VOID\r
8154 SetMachineThinkingEnables()\r
8155 {\r
8156   HMENU hMenu = GetMenu(hwndMain);\r
8157   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8158 \r
8159   SetMenuEnables(hMenu, machineThinkingEnables);\r
8160 \r
8161   if (gameMode == MachinePlaysBlack) {\r
8162     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8163   } else if (gameMode == MachinePlaysWhite) {\r
8164     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8165   } else if (gameMode == TwoMachinesPlay) {\r
8166     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8167   }\r
8168 }\r
8169 \r
8170 \r
8171 VOID\r
8172 DisplayTitle(char *str)\r
8173 {\r
8174   char title[MSG_SIZ], *host;\r
8175   if (str[0] != NULLCHAR) {\r
8176     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8177   } else if (appData.icsActive) {\r
8178     if (appData.icsCommPort[0] != NULLCHAR)\r
8179       host = "ICS";\r
8180     else \r
8181       host = appData.icsHost;\r
8182       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8183   } else if (appData.noChessProgram) {\r
8184     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8185   } else {\r
8186     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8187     strcat(title, ": ");\r
8188     strcat(title, first.tidy);\r
8189   }\r
8190   SetWindowText(hwndMain, title);\r
8191 }\r
8192 \r
8193 \r
8194 VOID\r
8195 DisplayMessage(char *str1, char *str2)\r
8196 {\r
8197   HDC hdc;\r
8198   HFONT oldFont;\r
8199   int remain = MESSAGE_TEXT_MAX - 1;\r
8200   int len;\r
8201 \r
8202   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8203   messageText[0] = NULLCHAR;\r
8204   if (*str1) {\r
8205     len = strlen(str1);\r
8206     if (len > remain) len = remain;\r
8207     strncpy(messageText, str1, len);\r
8208     messageText[len] = NULLCHAR;\r
8209     remain -= len;\r
8210   }\r
8211   if (*str2 && remain >= 2) {\r
8212     if (*str1) {\r
8213       strcat(messageText, "  ");\r
8214       remain -= 2;\r
8215     }\r
8216     len = strlen(str2);\r
8217     if (len > remain) len = remain;\r
8218     strncat(messageText, str2, len);\r
8219   }\r
8220   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8221   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8222 \r
8223   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8224 \r
8225   SAYMACHINEMOVE();\r
8226 \r
8227   hdc = GetDC(hwndMain);\r
8228   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8229   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8230              &messageRect, messageText, strlen(messageText), NULL);\r
8231   (void) SelectObject(hdc, oldFont);\r
8232   (void) ReleaseDC(hwndMain, hdc);\r
8233 }\r
8234 \r
8235 VOID\r
8236 DisplayError(char *str, int error)\r
8237 {\r
8238   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8239   int len;\r
8240 \r
8241   if (error == 0) {\r
8242     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8243   } else {\r
8244     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8245                         NULL, error, LANG_NEUTRAL,\r
8246                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8247     if (len > 0) {\r
8248       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8249     } else {\r
8250       ErrorMap *em = errmap;\r
8251       while (em->err != 0 && em->err != error) em++;\r
8252       if (em->err != 0) {\r
8253         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8254       } else {\r
8255         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8256       }\r
8257     }\r
8258   }\r
8259   \r
8260   ErrorPopUp(_("Error"), buf);\r
8261 }\r
8262 \r
8263 \r
8264 VOID\r
8265 DisplayMoveError(char *str)\r
8266 {\r
8267   fromX = fromY = -1;\r
8268   ClearHighlights();\r
8269   DrawPosition(FALSE, NULL);\r
8270   if (appData.popupMoveErrors) {\r
8271     ErrorPopUp(_("Error"), str);\r
8272   } else {\r
8273     DisplayMessage(str, "");\r
8274     moveErrorMessageUp = TRUE;\r
8275   }\r
8276 }\r
8277 \r
8278 VOID\r
8279 DisplayFatalError(char *str, int error, int exitStatus)\r
8280 {\r
8281   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8282   int len;\r
8283   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8284 \r
8285   if (error != 0) {\r
8286     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8287                         NULL, error, LANG_NEUTRAL,\r
8288                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8289     if (len > 0) {\r
8290       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8291     } else {\r
8292       ErrorMap *em = errmap;\r
8293       while (em->err != 0 && em->err != error) em++;\r
8294       if (em->err != 0) {\r
8295         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8296       } else {\r
8297         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8298       }\r
8299     }\r
8300     str = buf;\r
8301   }\r
8302   if (appData.debugMode) {\r
8303     fprintf(debugFP, "%s: %s\n", label, str);\r
8304   }\r
8305   if (appData.popupExitMessage) {\r
8306     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8307                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8308   }\r
8309   ExitEvent(exitStatus);\r
8310 }\r
8311 \r
8312 \r
8313 VOID\r
8314 DisplayInformation(char *str)\r
8315 {\r
8316   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8317 }\r
8318 \r
8319 \r
8320 VOID\r
8321 DisplayNote(char *str)\r
8322 {\r
8323   ErrorPopUp(_("Note"), str);\r
8324 }\r
8325 \r
8326 \r
8327 typedef struct {\r
8328   char *title, *question, *replyPrefix;\r
8329   ProcRef pr;\r
8330 } QuestionParams;\r
8331 \r
8332 LRESULT CALLBACK\r
8333 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8334 {\r
8335   static QuestionParams *qp;\r
8336   char reply[MSG_SIZ];\r
8337   int len, err;\r
8338 \r
8339   switch (message) {\r
8340   case WM_INITDIALOG:\r
8341     qp = (QuestionParams *) lParam;\r
8342     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8343     Translate(hDlg, DLG_Question);\r
8344     SetWindowText(hDlg, qp->title);\r
8345     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8346     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8347     return FALSE;\r
8348 \r
8349   case WM_COMMAND:\r
8350     switch (LOWORD(wParam)) {\r
8351     case IDOK:\r
8352       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8353       if (*reply) strcat(reply, " ");\r
8354       len = strlen(reply);\r
8355       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8356       strcat(reply, "\n");\r
8357       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8358       EndDialog(hDlg, TRUE);\r
8359       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8360       return TRUE;\r
8361     case IDCANCEL:\r
8362       EndDialog(hDlg, FALSE);\r
8363       return TRUE;\r
8364     default:\r
8365       break;\r
8366     }\r
8367     break;\r
8368   }\r
8369   return FALSE;\r
8370 }\r
8371 \r
8372 VOID\r
8373 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8374 {\r
8375     QuestionParams qp;\r
8376     FARPROC lpProc;\r
8377     \r
8378     qp.title = title;\r
8379     qp.question = question;\r
8380     qp.replyPrefix = replyPrefix;\r
8381     qp.pr = pr;\r
8382     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8383     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8384       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8385     FreeProcInstance(lpProc);\r
8386 }\r
8387 \r
8388 /* [AS] Pick FRC position */\r
8389 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8390 {\r
8391     static int * lpIndexFRC;\r
8392     BOOL index_is_ok;\r
8393     char buf[16];\r
8394 \r
8395     switch( message )\r
8396     {\r
8397     case WM_INITDIALOG:\r
8398         lpIndexFRC = (int *) lParam;\r
8399 \r
8400         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8401         Translate(hDlg, DLG_NewGameFRC);\r
8402 \r
8403         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8404         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8405         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8406         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8407 \r
8408         break;\r
8409 \r
8410     case WM_COMMAND:\r
8411         switch( LOWORD(wParam) ) {\r
8412         case IDOK:\r
8413             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8414             EndDialog( hDlg, 0 );\r
8415             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8416             return TRUE;\r
8417         case IDCANCEL:\r
8418             EndDialog( hDlg, 1 );   \r
8419             return TRUE;\r
8420         case IDC_NFG_Edit:\r
8421             if( HIWORD(wParam) == EN_CHANGE ) {\r
8422                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8423 \r
8424                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8425             }\r
8426             return TRUE;\r
8427         case IDC_NFG_Random:\r
8428           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8429             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8430             return TRUE;\r
8431         }\r
8432 \r
8433         break;\r
8434     }\r
8435 \r
8436     return FALSE;\r
8437 }\r
8438 \r
8439 int NewGameFRC()\r
8440 {\r
8441     int result;\r
8442     int index = appData.defaultFrcPosition;\r
8443     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8444 \r
8445     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8446 \r
8447     if( result == 0 ) {\r
8448         appData.defaultFrcPosition = index;\r
8449     }\r
8450 \r
8451     return result;\r
8452 }\r
8453 \r
8454 /* [AS] Game list options. Refactored by HGM */\r
8455 \r
8456 HWND gameListOptionsDialog;\r
8457 \r
8458 // low-level front-end: clear text edit / list widget\r
8459 void\r
8460 GLT_ClearList()\r
8461 {\r
8462     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8463 }\r
8464 \r
8465 // low-level front-end: clear text edit / list widget\r
8466 void\r
8467 GLT_DeSelectList()\r
8468 {\r
8469     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8470 }\r
8471 \r
8472 // low-level front-end: append line to text edit / list widget\r
8473 void\r
8474 GLT_AddToList( char *name )\r
8475 {\r
8476     if( name != 0 ) {\r
8477             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8478     }\r
8479 }\r
8480 \r
8481 // low-level front-end: get line from text edit / list widget\r
8482 Boolean\r
8483 GLT_GetFromList( int index, char *name )\r
8484 {\r
8485     if( name != 0 ) {\r
8486             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8487                 return TRUE;\r
8488     }\r
8489     return FALSE;\r
8490 }\r
8491 \r
8492 void GLT_MoveSelection( HWND hDlg, int delta )\r
8493 {\r
8494     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8495     int idx2 = idx1 + delta;\r
8496     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8497 \r
8498     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8499         char buf[128];\r
8500 \r
8501         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8502         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8503         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8504         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8505     }\r
8506 }\r
8507 \r
8508 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8509 {\r
8510     switch( message )\r
8511     {\r
8512     case WM_INITDIALOG:\r
8513         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8514         \r
8515         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8516         Translate(hDlg, DLG_GameListOptions);\r
8517 \r
8518         /* Initialize list */\r
8519         GLT_TagsToList( lpUserGLT );\r
8520 \r
8521         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8522 \r
8523         break;\r
8524 \r
8525     case WM_COMMAND:\r
8526         switch( LOWORD(wParam) ) {\r
8527         case IDOK:\r
8528             GLT_ParseList();\r
8529             EndDialog( hDlg, 0 );\r
8530             return TRUE;\r
8531         case IDCANCEL:\r
8532             EndDialog( hDlg, 1 );\r
8533             return TRUE;\r
8534 \r
8535         case IDC_GLT_Default:\r
8536             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8537             return TRUE;\r
8538 \r
8539         case IDC_GLT_Restore:\r
8540             GLT_TagsToList( appData.gameListTags );\r
8541             return TRUE;\r
8542 \r
8543         case IDC_GLT_Up:\r
8544             GLT_MoveSelection( hDlg, -1 );\r
8545             return TRUE;\r
8546 \r
8547         case IDC_GLT_Down:\r
8548             GLT_MoveSelection( hDlg, +1 );\r
8549             return TRUE;\r
8550         }\r
8551 \r
8552         break;\r
8553     }\r
8554 \r
8555     return FALSE;\r
8556 }\r
8557 \r
8558 int GameListOptions()\r
8559 {\r
8560     int result;\r
8561     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8562 \r
8563       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8564 \r
8565     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8566 \r
8567     if( result == 0 ) {\r
8568         /* [AS] Memory leak here! */\r
8569         appData.gameListTags = strdup( lpUserGLT ); \r
8570     }\r
8571 \r
8572     return result;\r
8573 }\r
8574 \r
8575 VOID\r
8576 DisplayIcsInteractionTitle(char *str)\r
8577 {\r
8578   char consoleTitle[MSG_SIZ];\r
8579 \r
8580     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8581     SetWindowText(hwndConsole, consoleTitle);\r
8582 \r
8583     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8584       char buf[MSG_SIZ], *p = buf, *q;\r
8585         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8586       do {\r
8587         q = strchr(p, ';');\r
8588         if(q) *q++ = 0;\r
8589         if(*p) ChatPopUp(p);\r
8590       } while(p=q);\r
8591     }\r
8592 \r
8593     SetActiveWindow(hwndMain);\r
8594 }\r
8595 \r
8596 void\r
8597 DrawPosition(int fullRedraw, Board board)\r
8598 {\r
8599   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8600 }\r
8601 \r
8602 void NotifyFrontendLogin()\r
8603 {\r
8604         if (hwndConsole)\r
8605                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8606 }\r
8607 \r
8608 VOID\r
8609 ResetFrontEnd()\r
8610 {\r
8611   fromX = fromY = -1;\r
8612   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8613     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8614     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8615     dragInfo.lastpos = dragInfo.pos;\r
8616     dragInfo.start.x = dragInfo.start.y = -1;\r
8617     dragInfo.from = dragInfo.start;\r
8618     ReleaseCapture();\r
8619     DrawPosition(TRUE, NULL);\r
8620   }\r
8621   TagsPopDown();\r
8622 }\r
8623 \r
8624 \r
8625 VOID\r
8626 CommentPopUp(char *title, char *str)\r
8627 {\r
8628   HWND hwnd = GetActiveWindow();\r
8629   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8630   SAY(str);\r
8631   SetActiveWindow(hwnd);\r
8632 }\r
8633 \r
8634 VOID\r
8635 CommentPopDown(void)\r
8636 {\r
8637   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8638   if (commentDialog) {\r
8639     ShowWindow(commentDialog, SW_HIDE);\r
8640   }\r
8641   commentUp = FALSE;\r
8642 }\r
8643 \r
8644 VOID\r
8645 EditCommentPopUp(int index, char *title, char *str)\r
8646 {\r
8647   EitherCommentPopUp(index, title, str, TRUE);\r
8648 }\r
8649 \r
8650 \r
8651 VOID\r
8652 RingBell()\r
8653 {\r
8654   MyPlaySound(&sounds[(int)SoundMove]);\r
8655 }\r
8656 \r
8657 VOID PlayIcsWinSound()\r
8658 {\r
8659   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8660 }\r
8661 \r
8662 VOID PlayIcsLossSound()\r
8663 {\r
8664   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8665 }\r
8666 \r
8667 VOID PlayIcsDrawSound()\r
8668 {\r
8669   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8670 }\r
8671 \r
8672 VOID PlayIcsUnfinishedSound()\r
8673 {\r
8674   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8675 }\r
8676 \r
8677 VOID\r
8678 PlayAlarmSound()\r
8679 {\r
8680   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8681 }\r
8682 \r
8683 VOID\r
8684 PlayTellSound()\r
8685 {\r
8686   MyPlaySound(&textAttribs[ColorTell].sound);\r
8687 }\r
8688 \r
8689 \r
8690 VOID\r
8691 EchoOn()\r
8692 {\r
8693   HWND hInput;\r
8694   consoleEcho = TRUE;\r
8695   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8696   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8697   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8698 }\r
8699 \r
8700 \r
8701 VOID\r
8702 EchoOff()\r
8703 {\r
8704   CHARFORMAT cf;\r
8705   HWND hInput;\r
8706   consoleEcho = FALSE;\r
8707   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8708   /* This works OK: set text and background both to the same color */\r
8709   cf = consoleCF;\r
8710   cf.crTextColor = COLOR_ECHOOFF;\r
8711   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8712   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8713 }\r
8714 \r
8715 /* No Raw()...? */\r
8716 \r
8717 void Colorize(ColorClass cc, int continuation)\r
8718 {\r
8719   currentColorClass = cc;\r
8720   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8721   consoleCF.crTextColor = textAttribs[cc].color;\r
8722   consoleCF.dwEffects = textAttribs[cc].effects;\r
8723   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8724 }\r
8725 \r
8726 char *\r
8727 UserName()\r
8728 {\r
8729   static char buf[MSG_SIZ];\r
8730   DWORD bufsiz = MSG_SIZ;\r
8731 \r
8732   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8733         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8734   }\r
8735   if (!GetUserName(buf, &bufsiz)) {\r
8736     /*DisplayError("Error getting user name", GetLastError());*/\r
8737     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8738   }\r
8739   return buf;\r
8740 }\r
8741 \r
8742 char *\r
8743 HostName()\r
8744 {\r
8745   static char buf[MSG_SIZ];\r
8746   DWORD bufsiz = MSG_SIZ;\r
8747 \r
8748   if (!GetComputerName(buf, &bufsiz)) {\r
8749     /*DisplayError("Error getting host name", GetLastError());*/\r
8750     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8751   }\r
8752   return buf;\r
8753 }\r
8754 \r
8755 \r
8756 int\r
8757 ClockTimerRunning()\r
8758 {\r
8759   return clockTimerEvent != 0;\r
8760 }\r
8761 \r
8762 int\r
8763 StopClockTimer()\r
8764 {\r
8765   if (clockTimerEvent == 0) return FALSE;\r
8766   KillTimer(hwndMain, clockTimerEvent);\r
8767   clockTimerEvent = 0;\r
8768   return TRUE;\r
8769 }\r
8770 \r
8771 void\r
8772 StartClockTimer(long millisec)\r
8773 {\r
8774   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8775                              (UINT) millisec, NULL);\r
8776 }\r
8777 \r
8778 void\r
8779 DisplayWhiteClock(long timeRemaining, int highlight)\r
8780 {\r
8781   HDC hdc;\r
8782   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8783 \r
8784   if(appData.noGUI) return;\r
8785   hdc = GetDC(hwndMain);\r
8786   if (!IsIconic(hwndMain)) {\r
8787     DisplayAClock(hdc, timeRemaining, highlight, \r
8788                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8789   }\r
8790   if (highlight && iconCurrent == iconBlack) {\r
8791     iconCurrent = iconWhite;\r
8792     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8793     if (IsIconic(hwndMain)) {\r
8794       DrawIcon(hdc, 2, 2, iconCurrent);\r
8795     }\r
8796   }\r
8797   (void) ReleaseDC(hwndMain, hdc);\r
8798   if (hwndConsole)\r
8799     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8800 }\r
8801 \r
8802 void\r
8803 DisplayBlackClock(long timeRemaining, int highlight)\r
8804 {\r
8805   HDC hdc;\r
8806   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8807 \r
8808   if(appData.noGUI) return;\r
8809   hdc = GetDC(hwndMain);\r
8810   if (!IsIconic(hwndMain)) {\r
8811     DisplayAClock(hdc, timeRemaining, highlight, \r
8812                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8813   }\r
8814   if (highlight && iconCurrent == iconWhite) {\r
8815     iconCurrent = iconBlack;\r
8816     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8817     if (IsIconic(hwndMain)) {\r
8818       DrawIcon(hdc, 2, 2, iconCurrent);\r
8819     }\r
8820   }\r
8821   (void) ReleaseDC(hwndMain, hdc);\r
8822   if (hwndConsole)\r
8823     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8824 }\r
8825 \r
8826 \r
8827 int\r
8828 LoadGameTimerRunning()\r
8829 {\r
8830   return loadGameTimerEvent != 0;\r
8831 }\r
8832 \r
8833 int\r
8834 StopLoadGameTimer()\r
8835 {\r
8836   if (loadGameTimerEvent == 0) return FALSE;\r
8837   KillTimer(hwndMain, loadGameTimerEvent);\r
8838   loadGameTimerEvent = 0;\r
8839   return TRUE;\r
8840 }\r
8841 \r
8842 void\r
8843 StartLoadGameTimer(long millisec)\r
8844 {\r
8845   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8846                                 (UINT) millisec, NULL);\r
8847 }\r
8848 \r
8849 void\r
8850 AutoSaveGame()\r
8851 {\r
8852   char *defName;\r
8853   FILE *f;\r
8854   char fileTitle[MSG_SIZ];\r
8855 \r
8856   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8857   f = OpenFileDialog(hwndMain, "a", defName,\r
8858                      appData.oldSaveStyle ? "gam" : "pgn",\r
8859                      GAME_FILT, \r
8860                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8861   if (f != NULL) {\r
8862     SaveGame(f, 0, "");\r
8863     fclose(f);\r
8864   }\r
8865 }\r
8866 \r
8867 \r
8868 void\r
8869 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8870 {\r
8871   if (delayedTimerEvent != 0) {\r
8872     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8873       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8874     }\r
8875     KillTimer(hwndMain, delayedTimerEvent);\r
8876     delayedTimerEvent = 0;\r
8877     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8878     delayedTimerCallback();\r
8879   }\r
8880   delayedTimerCallback = cb;\r
8881   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8882                                 (UINT) millisec, NULL);\r
8883 }\r
8884 \r
8885 DelayedEventCallback\r
8886 GetDelayedEvent()\r
8887 {\r
8888   if (delayedTimerEvent) {\r
8889     return delayedTimerCallback;\r
8890   } else {\r
8891     return NULL;\r
8892   }\r
8893 }\r
8894 \r
8895 void\r
8896 CancelDelayedEvent()\r
8897 {\r
8898   if (delayedTimerEvent) {\r
8899     KillTimer(hwndMain, delayedTimerEvent);\r
8900     delayedTimerEvent = 0;\r
8901   }\r
8902 }\r
8903 \r
8904 DWORD GetWin32Priority(int nice)\r
8905 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8906 /*\r
8907 REALTIME_PRIORITY_CLASS     0x00000100\r
8908 HIGH_PRIORITY_CLASS         0x00000080\r
8909 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8910 NORMAL_PRIORITY_CLASS       0x00000020\r
8911 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8912 IDLE_PRIORITY_CLASS         0x00000040\r
8913 */\r
8914         if (nice < -15) return 0x00000080;\r
8915         if (nice < 0)   return 0x00008000;\r
8916         if (nice == 0)  return 0x00000020;\r
8917         if (nice < 15)  return 0x00004000;\r
8918         return 0x00000040;\r
8919 }\r
8920 \r
8921 void RunCommand(char *cmdLine)\r
8922 {\r
8923   /* Now create the child process. */\r
8924   STARTUPINFO siStartInfo;\r
8925   PROCESS_INFORMATION piProcInfo;\r
8926 \r
8927   siStartInfo.cb = sizeof(STARTUPINFO);\r
8928   siStartInfo.lpReserved = NULL;\r
8929   siStartInfo.lpDesktop = NULL;\r
8930   siStartInfo.lpTitle = NULL;\r
8931   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8932   siStartInfo.cbReserved2 = 0;\r
8933   siStartInfo.lpReserved2 = NULL;\r
8934   siStartInfo.hStdInput = NULL;\r
8935   siStartInfo.hStdOutput = NULL;\r
8936   siStartInfo.hStdError = NULL;\r
8937 \r
8938   CreateProcess(NULL,\r
8939                 cmdLine,           /* command line */\r
8940                 NULL,      /* process security attributes */\r
8941                 NULL,      /* primary thread security attrs */\r
8942                 TRUE,      /* handles are inherited */\r
8943                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8944                 NULL,      /* use parent's environment */\r
8945                 NULL,\r
8946                 &siStartInfo, /* STARTUPINFO pointer */\r
8947                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8948 \r
8949   CloseHandle(piProcInfo.hThread);\r
8950 }\r
8951 \r
8952 /* Start a child process running the given program.\r
8953    The process's standard output can be read from "from", and its\r
8954    standard input can be written to "to".\r
8955    Exit with fatal error if anything goes wrong.\r
8956    Returns an opaque pointer that can be used to destroy the process\r
8957    later.\r
8958 */\r
8959 int\r
8960 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8961 {\r
8962 #define BUFSIZE 4096\r
8963 \r
8964   HANDLE hChildStdinRd, hChildStdinWr,\r
8965     hChildStdoutRd, hChildStdoutWr;\r
8966   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8967   SECURITY_ATTRIBUTES saAttr;\r
8968   BOOL fSuccess;\r
8969   PROCESS_INFORMATION piProcInfo;\r
8970   STARTUPINFO siStartInfo;\r
8971   ChildProc *cp;\r
8972   char buf[MSG_SIZ];\r
8973   DWORD err;\r
8974 \r
8975   if (appData.debugMode) {\r
8976     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8977   }\r
8978 \r
8979   *pr = NoProc;\r
8980 \r
8981   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8982   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8983   saAttr.bInheritHandle = TRUE;\r
8984   saAttr.lpSecurityDescriptor = NULL;\r
8985 \r
8986   /*\r
8987    * The steps for redirecting child's STDOUT:\r
8988    *     1. Create anonymous pipe to be STDOUT for child.\r
8989    *     2. Create a noninheritable duplicate of read handle,\r
8990    *         and close the inheritable read handle.\r
8991    */\r
8992 \r
8993   /* Create a pipe for the child's STDOUT. */\r
8994   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8995     return GetLastError();\r
8996   }\r
8997 \r
8998   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8999   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9000                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9001                              FALSE,     /* not inherited */\r
9002                              DUPLICATE_SAME_ACCESS);\r
9003   if (! fSuccess) {\r
9004     return GetLastError();\r
9005   }\r
9006   CloseHandle(hChildStdoutRd);\r
9007 \r
9008   /*\r
9009    * The steps for redirecting child's STDIN:\r
9010    *     1. Create anonymous pipe to be STDIN for child.\r
9011    *     2. Create a noninheritable duplicate of write handle,\r
9012    *         and close the inheritable write handle.\r
9013    */\r
9014 \r
9015   /* Create a pipe for the child's STDIN. */\r
9016   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9017     return GetLastError();\r
9018   }\r
9019 \r
9020   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9021   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9022                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9023                              FALSE,     /* not inherited */\r
9024                              DUPLICATE_SAME_ACCESS);\r
9025   if (! fSuccess) {\r
9026     return GetLastError();\r
9027   }\r
9028   CloseHandle(hChildStdinWr);\r
9029 \r
9030   /* Arrange to (1) look in dir for the child .exe file, and\r
9031    * (2) have dir be the child's working directory.  Interpret\r
9032    * dir relative to the directory WinBoard loaded from. */\r
9033   GetCurrentDirectory(MSG_SIZ, buf);\r
9034   SetCurrentDirectory(installDir);\r
9035   SetCurrentDirectory(dir);\r
9036 \r
9037   /* Now create the child process. */\r
9038 \r
9039   siStartInfo.cb = sizeof(STARTUPINFO);\r
9040   siStartInfo.lpReserved = NULL;\r
9041   siStartInfo.lpDesktop = NULL;\r
9042   siStartInfo.lpTitle = NULL;\r
9043   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9044   siStartInfo.cbReserved2 = 0;\r
9045   siStartInfo.lpReserved2 = NULL;\r
9046   siStartInfo.hStdInput = hChildStdinRd;\r
9047   siStartInfo.hStdOutput = hChildStdoutWr;\r
9048   siStartInfo.hStdError = hChildStdoutWr;\r
9049 \r
9050   fSuccess = CreateProcess(NULL,\r
9051                            cmdLine,        /* command line */\r
9052                            NULL,           /* process security attributes */\r
9053                            NULL,           /* primary thread security attrs */\r
9054                            TRUE,           /* handles are inherited */\r
9055                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9056                            NULL,           /* use parent's environment */\r
9057                            NULL,\r
9058                            &siStartInfo, /* STARTUPINFO pointer */\r
9059                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9060 \r
9061   err = GetLastError();\r
9062   SetCurrentDirectory(buf); /* return to prev directory */\r
9063   if (! fSuccess) {\r
9064     return err;\r
9065   }\r
9066 \r
9067   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9068     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9069     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9070   }\r
9071 \r
9072   /* Close the handles we don't need in the parent */\r
9073   CloseHandle(piProcInfo.hThread);\r
9074   CloseHandle(hChildStdinRd);\r
9075   CloseHandle(hChildStdoutWr);\r
9076 \r
9077   /* Prepare return value */\r
9078   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9079   cp->kind = CPReal;\r
9080   cp->hProcess = piProcInfo.hProcess;\r
9081   cp->pid = piProcInfo.dwProcessId;\r
9082   cp->hFrom = hChildStdoutRdDup;\r
9083   cp->hTo = hChildStdinWrDup;\r
9084 \r
9085   *pr = (void *) cp;\r
9086 \r
9087   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9088      2000 where engines sometimes don't see the initial command(s)\r
9089      from WinBoard and hang.  I don't understand how that can happen,\r
9090      but the Sleep is harmless, so I've put it in.  Others have also\r
9091      reported what may be the same problem, so hopefully this will fix\r
9092      it for them too.  */\r
9093   Sleep(500);\r
9094 \r
9095   return NO_ERROR;\r
9096 }\r
9097 \r
9098 \r
9099 void\r
9100 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9101 {\r
9102   ChildProc *cp; int result;\r
9103 \r
9104   cp = (ChildProc *) pr;\r
9105   if (cp == NULL) return;\r
9106 \r
9107   switch (cp->kind) {\r
9108   case CPReal:\r
9109     /* TerminateProcess is considered harmful, so... */\r
9110     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9111     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9112     /* The following doesn't work because the chess program\r
9113        doesn't "have the same console" as WinBoard.  Maybe\r
9114        we could arrange for this even though neither WinBoard\r
9115        nor the chess program uses a console for stdio? */\r
9116     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9117 \r
9118     /* [AS] Special termination modes for misbehaving programs... */\r
9119     if( signal == 9 ) { \r
9120         result = TerminateProcess( cp->hProcess, 0 );\r
9121 \r
9122         if ( appData.debugMode) {\r
9123             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9124         }\r
9125     }\r
9126     else if( signal == 10 ) {\r
9127         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9128 \r
9129         if( dw != WAIT_OBJECT_0 ) {\r
9130             result = TerminateProcess( cp->hProcess, 0 );\r
9131 \r
9132             if ( appData.debugMode) {\r
9133                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9134             }\r
9135 \r
9136         }\r
9137     }\r
9138 \r
9139     CloseHandle(cp->hProcess);\r
9140     break;\r
9141 \r
9142   case CPComm:\r
9143     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9144     break;\r
9145 \r
9146   case CPSock:\r
9147     closesocket(cp->sock);\r
9148     WSACleanup();\r
9149     break;\r
9150 \r
9151   case CPRcmd:\r
9152     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9153     closesocket(cp->sock);\r
9154     closesocket(cp->sock2);\r
9155     WSACleanup();\r
9156     break;\r
9157   }\r
9158   free(cp);\r
9159 }\r
9160 \r
9161 void\r
9162 InterruptChildProcess(ProcRef pr)\r
9163 {\r
9164   ChildProc *cp;\r
9165 \r
9166   cp = (ChildProc *) pr;\r
9167   if (cp == NULL) return;\r
9168   switch (cp->kind) {\r
9169   case CPReal:\r
9170     /* The following doesn't work because the chess program\r
9171        doesn't "have the same console" as WinBoard.  Maybe\r
9172        we could arrange for this even though neither WinBoard\r
9173        nor the chess program uses a console for stdio */\r
9174     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9175     break;\r
9176 \r
9177   case CPComm:\r
9178   case CPSock:\r
9179     /* Can't interrupt */\r
9180     break;\r
9181 \r
9182   case CPRcmd:\r
9183     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9184     break;\r
9185   }\r
9186 }\r
9187 \r
9188 \r
9189 int\r
9190 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9191 {\r
9192   char cmdLine[MSG_SIZ];\r
9193 \r
9194   if (port[0] == NULLCHAR) {\r
9195     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9196   } else {\r
9197     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9198   }\r
9199   return StartChildProcess(cmdLine, "", pr);\r
9200 }\r
9201 \r
9202 \r
9203 /* Code to open TCP sockets */\r
9204 \r
9205 int\r
9206 OpenTCP(char *host, char *port, ProcRef *pr)\r
9207 {\r
9208   ChildProc *cp;\r
9209   int err;\r
9210   SOCKET s;\r
9211 \r
9212   struct sockaddr_in sa, mysa;\r
9213   struct hostent FAR *hp;\r
9214   unsigned short uport;\r
9215   WORD wVersionRequested;\r
9216   WSADATA wsaData;\r
9217 \r
9218   /* Initialize socket DLL */\r
9219   wVersionRequested = MAKEWORD(1, 1);\r
9220   err = WSAStartup(wVersionRequested, &wsaData);\r
9221   if (err != 0) return err;\r
9222 \r
9223   /* Make socket */\r
9224   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9225     err = WSAGetLastError();\r
9226     WSACleanup();\r
9227     return err;\r
9228   }\r
9229 \r
9230   /* Bind local address using (mostly) don't-care values.\r
9231    */\r
9232   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9233   mysa.sin_family = AF_INET;\r
9234   mysa.sin_addr.s_addr = INADDR_ANY;\r
9235   uport = (unsigned short) 0;\r
9236   mysa.sin_port = htons(uport);\r
9237   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9238       == SOCKET_ERROR) {\r
9239     err = WSAGetLastError();\r
9240     WSACleanup();\r
9241     return err;\r
9242   }\r
9243 \r
9244   /* Resolve remote host name */\r
9245   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9246   if (!(hp = gethostbyname(host))) {\r
9247     unsigned int b0, b1, b2, b3;\r
9248 \r
9249     err = WSAGetLastError();\r
9250 \r
9251     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9252       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9253       hp->h_addrtype = AF_INET;\r
9254       hp->h_length = 4;\r
9255       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9256       hp->h_addr_list[0] = (char *) malloc(4);\r
9257       hp->h_addr_list[0][0] = (char) b0;\r
9258       hp->h_addr_list[0][1] = (char) b1;\r
9259       hp->h_addr_list[0][2] = (char) b2;\r
9260       hp->h_addr_list[0][3] = (char) b3;\r
9261     } else {\r
9262       WSACleanup();\r
9263       return err;\r
9264     }\r
9265   }\r
9266   sa.sin_family = hp->h_addrtype;\r
9267   uport = (unsigned short) atoi(port);\r
9268   sa.sin_port = htons(uport);\r
9269   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9270 \r
9271   /* Make connection */\r
9272   if (connect(s, (struct sockaddr *) &sa,\r
9273               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9274     err = WSAGetLastError();\r
9275     WSACleanup();\r
9276     return err;\r
9277   }\r
9278 \r
9279   /* Prepare return value */\r
9280   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9281   cp->kind = CPSock;\r
9282   cp->sock = s;\r
9283   *pr = (ProcRef *) cp;\r
9284 \r
9285   return NO_ERROR;\r
9286 }\r
9287 \r
9288 int\r
9289 OpenCommPort(char *name, ProcRef *pr)\r
9290 {\r
9291   HANDLE h;\r
9292   COMMTIMEOUTS ct;\r
9293   ChildProc *cp;\r
9294   char fullname[MSG_SIZ];\r
9295 \r
9296   if (*name != '\\')\r
9297     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9298   else\r
9299     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9300 \r
9301   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9302                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9303   if (h == (HANDLE) -1) {\r
9304     return GetLastError();\r
9305   }\r
9306   hCommPort = h;\r
9307 \r
9308   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9309 \r
9310   /* Accumulate characters until a 100ms pause, then parse */\r
9311   ct.ReadIntervalTimeout = 100;\r
9312   ct.ReadTotalTimeoutMultiplier = 0;\r
9313   ct.ReadTotalTimeoutConstant = 0;\r
9314   ct.WriteTotalTimeoutMultiplier = 0;\r
9315   ct.WriteTotalTimeoutConstant = 0;\r
9316   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9317 \r
9318   /* Prepare return value */\r
9319   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9320   cp->kind = CPComm;\r
9321   cp->hFrom = h;\r
9322   cp->hTo = h;\r
9323   *pr = (ProcRef *) cp;\r
9324 \r
9325   return NO_ERROR;\r
9326 }\r
9327 \r
9328 int\r
9329 OpenLoopback(ProcRef *pr)\r
9330 {\r
9331   DisplayFatalError(_("Not implemented"), 0, 1);\r
9332   return NO_ERROR;\r
9333 }\r
9334 \r
9335 \r
9336 int\r
9337 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9338 {\r
9339   ChildProc *cp;\r
9340   int err;\r
9341   SOCKET s, s2, s3;\r
9342   struct sockaddr_in sa, mysa;\r
9343   struct hostent FAR *hp;\r
9344   unsigned short uport;\r
9345   WORD wVersionRequested;\r
9346   WSADATA wsaData;\r
9347   int fromPort;\r
9348   char stderrPortStr[MSG_SIZ];\r
9349 \r
9350   /* Initialize socket DLL */\r
9351   wVersionRequested = MAKEWORD(1, 1);\r
9352   err = WSAStartup(wVersionRequested, &wsaData);\r
9353   if (err != 0) return err;\r
9354 \r
9355   /* Resolve remote host name */\r
9356   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9357   if (!(hp = gethostbyname(host))) {\r
9358     unsigned int b0, b1, b2, b3;\r
9359 \r
9360     err = WSAGetLastError();\r
9361 \r
9362     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9363       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9364       hp->h_addrtype = AF_INET;\r
9365       hp->h_length = 4;\r
9366       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9367       hp->h_addr_list[0] = (char *) malloc(4);\r
9368       hp->h_addr_list[0][0] = (char) b0;\r
9369       hp->h_addr_list[0][1] = (char) b1;\r
9370       hp->h_addr_list[0][2] = (char) b2;\r
9371       hp->h_addr_list[0][3] = (char) b3;\r
9372     } else {\r
9373       WSACleanup();\r
9374       return err;\r
9375     }\r
9376   }\r
9377   sa.sin_family = hp->h_addrtype;\r
9378   uport = (unsigned short) 514;\r
9379   sa.sin_port = htons(uport);\r
9380   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9381 \r
9382   /* Bind local socket to unused "privileged" port address\r
9383    */\r
9384   s = INVALID_SOCKET;\r
9385   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9386   mysa.sin_family = AF_INET;\r
9387   mysa.sin_addr.s_addr = INADDR_ANY;\r
9388   for (fromPort = 1023;; fromPort--) {\r
9389     if (fromPort < 0) {\r
9390       WSACleanup();\r
9391       return WSAEADDRINUSE;\r
9392     }\r
9393     if (s == INVALID_SOCKET) {\r
9394       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9395         err = WSAGetLastError();\r
9396         WSACleanup();\r
9397         return err;\r
9398       }\r
9399     }\r
9400     uport = (unsigned short) fromPort;\r
9401     mysa.sin_port = htons(uport);\r
9402     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9403         == SOCKET_ERROR) {\r
9404       err = WSAGetLastError();\r
9405       if (err == WSAEADDRINUSE) continue;\r
9406       WSACleanup();\r
9407       return err;\r
9408     }\r
9409     if (connect(s, (struct sockaddr *) &sa,\r
9410       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9411       err = WSAGetLastError();\r
9412       if (err == WSAEADDRINUSE) {\r
9413         closesocket(s);\r
9414         s = -1;\r
9415         continue;\r
9416       }\r
9417       WSACleanup();\r
9418       return err;\r
9419     }\r
9420     break;\r
9421   }\r
9422 \r
9423   /* Bind stderr local socket to unused "privileged" port address\r
9424    */\r
9425   s2 = INVALID_SOCKET;\r
9426   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9427   mysa.sin_family = AF_INET;\r
9428   mysa.sin_addr.s_addr = INADDR_ANY;\r
9429   for (fromPort = 1023;; fromPort--) {\r
9430     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9431     if (fromPort < 0) {\r
9432       (void) closesocket(s);\r
9433       WSACleanup();\r
9434       return WSAEADDRINUSE;\r
9435     }\r
9436     if (s2 == INVALID_SOCKET) {\r
9437       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9438         err = WSAGetLastError();\r
9439         closesocket(s);\r
9440         WSACleanup();\r
9441         return err;\r
9442       }\r
9443     }\r
9444     uport = (unsigned short) fromPort;\r
9445     mysa.sin_port = htons(uport);\r
9446     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9447         == SOCKET_ERROR) {\r
9448       err = WSAGetLastError();\r
9449       if (err == WSAEADDRINUSE) continue;\r
9450       (void) closesocket(s);\r
9451       WSACleanup();\r
9452       return err;\r
9453     }\r
9454     if (listen(s2, 1) == SOCKET_ERROR) {\r
9455       err = WSAGetLastError();\r
9456       if (err == WSAEADDRINUSE) {\r
9457         closesocket(s2);\r
9458         s2 = INVALID_SOCKET;\r
9459         continue;\r
9460       }\r
9461       (void) closesocket(s);\r
9462       (void) closesocket(s2);\r
9463       WSACleanup();\r
9464       return err;\r
9465     }\r
9466     break;\r
9467   }\r
9468   prevStderrPort = fromPort; // remember port used\r
9469   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9470 \r
9471   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9472     err = WSAGetLastError();\r
9473     (void) closesocket(s);\r
9474     (void) closesocket(s2);\r
9475     WSACleanup();\r
9476     return err;\r
9477   }\r
9478 \r
9479   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9480     err = WSAGetLastError();\r
9481     (void) closesocket(s);\r
9482     (void) closesocket(s2);\r
9483     WSACleanup();\r
9484     return err;\r
9485   }\r
9486   if (*user == NULLCHAR) user = UserName();\r
9487   if (send(s, user, strlen(user) + 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   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9495     err = WSAGetLastError();\r
9496     (void) closesocket(s);\r
9497     (void) closesocket(s2);\r
9498     WSACleanup();\r
9499     return err;\r
9500   }\r
9501 \r
9502   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9503     err = WSAGetLastError();\r
9504     (void) closesocket(s);\r
9505     (void) closesocket(s2);\r
9506     WSACleanup();\r
9507     return err;\r
9508   }\r
9509   (void) closesocket(s2);  /* Stop listening */\r
9510 \r
9511   /* Prepare return value */\r
9512   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9513   cp->kind = CPRcmd;\r
9514   cp->sock = s;\r
9515   cp->sock2 = s3;\r
9516   *pr = (ProcRef *) cp;\r
9517 \r
9518   return NO_ERROR;\r
9519 }\r
9520 \r
9521 \r
9522 InputSourceRef\r
9523 AddInputSource(ProcRef pr, int lineByLine,\r
9524                InputCallback func, VOIDSTAR closure)\r
9525 {\r
9526   InputSource *is, *is2 = NULL;\r
9527   ChildProc *cp = (ChildProc *) pr;\r
9528 \r
9529   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9530   is->lineByLine = lineByLine;\r
9531   is->func = func;\r
9532   is->closure = closure;\r
9533   is->second = NULL;\r
9534   is->next = is->buf;\r
9535   if (pr == NoProc) {\r
9536     is->kind = CPReal;\r
9537     consoleInputSource = is;\r
9538   } else {\r
9539     is->kind = cp->kind;\r
9540     /* \r
9541         [AS] Try to avoid a race condition if the thread is given control too early:\r
9542         we create all threads suspended so that the is->hThread variable can be\r
9543         safely assigned, then let the threads start with ResumeThread.\r
9544     */\r
9545     switch (cp->kind) {\r
9546     case CPReal:\r
9547       is->hFile = cp->hFrom;\r
9548       cp->hFrom = NULL; /* now owned by InputThread */\r
9549       is->hThread =\r
9550         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9551                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9552       break;\r
9553 \r
9554     case CPComm:\r
9555       is->hFile = cp->hFrom;\r
9556       cp->hFrom = NULL; /* now owned by InputThread */\r
9557       is->hThread =\r
9558         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9559                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9560       break;\r
9561 \r
9562     case CPSock:\r
9563       is->sock = cp->sock;\r
9564       is->hThread =\r
9565         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9566                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9567       break;\r
9568 \r
9569     case CPRcmd:\r
9570       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9571       *is2 = *is;\r
9572       is->sock = cp->sock;\r
9573       is->second = is2;\r
9574       is2->sock = cp->sock2;\r
9575       is2->second = is2;\r
9576       is->hThread =\r
9577         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9578                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9579       is2->hThread =\r
9580         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9581                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9582       break;\r
9583     }\r
9584 \r
9585     if( is->hThread != NULL ) {\r
9586         ResumeThread( is->hThread );\r
9587     }\r
9588 \r
9589     if( is2 != NULL && is2->hThread != NULL ) {\r
9590         ResumeThread( is2->hThread );\r
9591     }\r
9592   }\r
9593 \r
9594   return (InputSourceRef) is;\r
9595 }\r
9596 \r
9597 void\r
9598 RemoveInputSource(InputSourceRef isr)\r
9599 {\r
9600   InputSource *is;\r
9601 \r
9602   is = (InputSource *) isr;\r
9603   is->hThread = NULL;  /* tell thread to stop */\r
9604   CloseHandle(is->hThread);\r
9605   if (is->second != NULL) {\r
9606     is->second->hThread = NULL;\r
9607     CloseHandle(is->second->hThread);\r
9608   }\r
9609 }\r
9610 \r
9611 int no_wrap(char *message, int count)\r
9612 {\r
9613     ConsoleOutput(message, count, FALSE);\r
9614     return count;\r
9615 }\r
9616 \r
9617 int\r
9618 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9619 {\r
9620   DWORD dOutCount;\r
9621   int outCount = SOCKET_ERROR;\r
9622   ChildProc *cp = (ChildProc *) pr;\r
9623   static OVERLAPPED ovl;\r
9624   static int line = 0;\r
9625 \r
9626   if (pr == NoProc)\r
9627   {\r
9628     if (appData.noJoin || !appData.useInternalWrap)\r
9629       return no_wrap(message, count);\r
9630     else\r
9631     {\r
9632       int width = get_term_width();\r
9633       int len = wrap(NULL, message, count, width, &line);\r
9634       char *msg = malloc(len);\r
9635       int dbgchk;\r
9636 \r
9637       if (!msg)\r
9638         return no_wrap(message, count);\r
9639       else\r
9640       {\r
9641         dbgchk = wrap(msg, message, count, width, &line);\r
9642         if (dbgchk != len && appData.debugMode)\r
9643             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9644         ConsoleOutput(msg, len, FALSE);\r
9645         free(msg);\r
9646         return len;\r
9647       }\r
9648     }\r
9649   }\r
9650 \r
9651   if (ovl.hEvent == NULL) {\r
9652     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9653   }\r
9654   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9655 \r
9656   switch (cp->kind) {\r
9657   case CPSock:\r
9658   case CPRcmd:\r
9659     outCount = send(cp->sock, message, count, 0);\r
9660     if (outCount == SOCKET_ERROR) {\r
9661       *outError = WSAGetLastError();\r
9662     } else {\r
9663       *outError = NO_ERROR;\r
9664     }\r
9665     break;\r
9666 \r
9667   case CPReal:\r
9668     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9669                   &dOutCount, NULL)) {\r
9670       *outError = NO_ERROR;\r
9671       outCount = (int) dOutCount;\r
9672     } else {\r
9673       *outError = GetLastError();\r
9674     }\r
9675     break;\r
9676 \r
9677   case CPComm:\r
9678     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9679                             &dOutCount, &ovl);\r
9680     if (*outError == NO_ERROR) {\r
9681       outCount = (int) dOutCount;\r
9682     }\r
9683     break;\r
9684   }\r
9685   return outCount;\r
9686 }\r
9687 \r
9688 void\r
9689 DoSleep(int n)\r
9690 {\r
9691     if(n != 0) Sleep(n);\r
9692 }\r
9693 \r
9694 int\r
9695 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9696                        long msdelay)\r
9697 {\r
9698   /* Ignore delay, not implemented for WinBoard */\r
9699   return OutputToProcess(pr, message, count, outError);\r
9700 }\r
9701 \r
9702 \r
9703 void\r
9704 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9705                         char *buf, int count, int error)\r
9706 {\r
9707   DisplayFatalError(_("Not implemented"), 0, 1);\r
9708 }\r
9709 \r
9710 /* see wgamelist.c for Game List functions */\r
9711 /* see wedittags.c for Edit Tags functions */\r
9712 \r
9713 \r
9714 VOID\r
9715 ICSInitScript()\r
9716 {\r
9717   FILE *f;\r
9718   char buf[MSG_SIZ];\r
9719   char *dummy;\r
9720 \r
9721   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9722     f = fopen(buf, "r");\r
9723     if (f != NULL) {\r
9724       ProcessICSInitScript(f);\r
9725       fclose(f);\r
9726     }\r
9727   }\r
9728 }\r
9729 \r
9730 \r
9731 VOID\r
9732 StartAnalysisClock()\r
9733 {\r
9734   if (analysisTimerEvent) return;\r
9735   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9736                                         (UINT) 2000, NULL);\r
9737 }\r
9738 \r
9739 VOID\r
9740 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9741 {\r
9742   highlightInfo.sq[0].x = fromX;\r
9743   highlightInfo.sq[0].y = fromY;\r
9744   highlightInfo.sq[1].x = toX;\r
9745   highlightInfo.sq[1].y = toY;\r
9746 }\r
9747 \r
9748 VOID\r
9749 ClearHighlights()\r
9750 {\r
9751   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9752     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9753 }\r
9754 \r
9755 VOID\r
9756 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9757 {\r
9758   premoveHighlightInfo.sq[0].x = fromX;\r
9759   premoveHighlightInfo.sq[0].y = fromY;\r
9760   premoveHighlightInfo.sq[1].x = toX;\r
9761   premoveHighlightInfo.sq[1].y = toY;\r
9762 }\r
9763 \r
9764 VOID\r
9765 ClearPremoveHighlights()\r
9766 {\r
9767   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9768     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9769 }\r
9770 \r
9771 VOID\r
9772 ShutDownFrontEnd()\r
9773 {\r
9774   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9775   DeleteClipboardTempFiles();\r
9776 }\r
9777 \r
9778 void\r
9779 BoardToTop()\r
9780 {\r
9781     if (IsIconic(hwndMain))\r
9782       ShowWindow(hwndMain, SW_RESTORE);\r
9783 \r
9784     SetActiveWindow(hwndMain);\r
9785 }\r
9786 \r
9787 /*\r
9788  * Prototypes for animation support routines\r
9789  */\r
9790 static void ScreenSquare(int column, int row, POINT * pt);\r
9791 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9792      POINT frames[], int * nFrames);\r
9793 \r
9794 \r
9795 #define kFactor 4\r
9796 \r
9797 void\r
9798 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9799 {       // [HGM] atomic: animate blast wave\r
9800         int i;\r
9801 \r
9802         explodeInfo.fromX = fromX;\r
9803         explodeInfo.fromY = fromY;\r
9804         explodeInfo.toX = toX;\r
9805         explodeInfo.toY = toY;\r
9806         for(i=1; i<4*kFactor; i++) {\r
9807             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9808             DrawPosition(FALSE, board);\r
9809             Sleep(appData.animSpeed);\r
9810         }\r
9811         explodeInfo.radius = 0;\r
9812         DrawPosition(TRUE, board);\r
9813 }\r
9814 \r
9815 void\r
9816 AnimateMove(board, fromX, fromY, toX, toY)\r
9817      Board board;\r
9818      int fromX;\r
9819      int fromY;\r
9820      int toX;\r
9821      int toY;\r
9822 {\r
9823   ChessSquare piece;\r
9824   POINT start, finish, mid;\r
9825   POINT frames[kFactor * 2 + 1];\r
9826   int nFrames, n;\r
9827 \r
9828   if (!appData.animate) return;\r
9829   if (doingSizing) return;\r
9830   if (fromY < 0 || fromX < 0) return;\r
9831   piece = board[fromY][fromX];\r
9832   if (piece >= EmptySquare) return;\r
9833 \r
9834   ScreenSquare(fromX, fromY, &start);\r
9835   ScreenSquare(toX, toY, &finish);\r
9836 \r
9837   /* All moves except knight jumps move in straight line */\r
9838   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9839     mid.x = start.x + (finish.x - start.x) / 2;\r
9840     mid.y = start.y + (finish.y - start.y) / 2;\r
9841   } else {\r
9842     /* Knight: make straight movement then diagonal */\r
9843     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9844        mid.x = start.x + (finish.x - start.x) / 2;\r
9845        mid.y = start.y;\r
9846      } else {\r
9847        mid.x = start.x;\r
9848        mid.y = start.y + (finish.y - start.y) / 2;\r
9849      }\r
9850   }\r
9851   \r
9852   /* Don't use as many frames for very short moves */\r
9853   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9854     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9855   else\r
9856     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9857 \r
9858   animInfo.from.x = fromX;\r
9859   animInfo.from.y = fromY;\r
9860   animInfo.to.x = toX;\r
9861   animInfo.to.y = toY;\r
9862   animInfo.lastpos = start;\r
9863   animInfo.piece = piece;\r
9864   for (n = 0; n < nFrames; n++) {\r
9865     animInfo.pos = frames[n];\r
9866     DrawPosition(FALSE, NULL);\r
9867     animInfo.lastpos = animInfo.pos;\r
9868     Sleep(appData.animSpeed);\r
9869   }\r
9870   animInfo.pos = finish;\r
9871   DrawPosition(FALSE, NULL);\r
9872   animInfo.piece = EmptySquare;\r
9873   Explode(board, fromX, fromY, toX, toY);\r
9874 }\r
9875 \r
9876 /*      Convert board position to corner of screen rect and color       */\r
9877 \r
9878 static void\r
9879 ScreenSquare(column, row, pt)\r
9880      int column; int row; POINT * pt;\r
9881 {\r
9882   if (flipView) {\r
9883     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9884     pt->y = lineGap + row * (squareSize + lineGap);\r
9885   } else {\r
9886     pt->x = lineGap + column * (squareSize + lineGap);\r
9887     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9888   }\r
9889 }\r
9890 \r
9891 /*      Generate a series of frame coords from start->mid->finish.\r
9892         The movement rate doubles until the half way point is\r
9893         reached, then halves back down to the final destination,\r
9894         which gives a nice slow in/out effect. The algorithmn\r
9895         may seem to generate too many intermediates for short\r
9896         moves, but remember that the purpose is to attract the\r
9897         viewers attention to the piece about to be moved and\r
9898         then to where it ends up. Too few frames would be less\r
9899         noticeable.                                             */\r
9900 \r
9901 static void\r
9902 Tween(start, mid, finish, factor, frames, nFrames)\r
9903      POINT * start; POINT * mid;\r
9904      POINT * finish; int factor;\r
9905      POINT frames[]; int * nFrames;\r
9906 {\r
9907   int n, fraction = 1, count = 0;\r
9908 \r
9909   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9910   for (n = 0; n < factor; n++)\r
9911     fraction *= 2;\r
9912   for (n = 0; n < factor; n++) {\r
9913     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9914     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9915     count ++;\r
9916     fraction = fraction / 2;\r
9917   }\r
9918   \r
9919   /* Midpoint */\r
9920   frames[count] = *mid;\r
9921   count ++;\r
9922   \r
9923   /* Slow out, stepping 1/2, then 1/4, ... */\r
9924   fraction = 2;\r
9925   for (n = 0; n < factor; n++) {\r
9926     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9927     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9928     count ++;\r
9929     fraction = fraction * 2;\r
9930   }\r
9931   *nFrames = count;\r
9932 }\r
9933 \r
9934 void\r
9935 SettingsPopUp(ChessProgramState *cps)\r
9936 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9937       EngineOptionsPopup(savedHwnd, cps);\r
9938 }\r
9939 \r
9940 int flock(int fid, int code)\r
9941 {\r
9942     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9943     OVERLAPPED ov;\r
9944     ov.hEvent = NULL;\r
9945     ov.Offset = 0;\r
9946     ov.OffsetHigh = 0;\r
9947     switch(code) {\r
9948       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9949       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9950       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9951       default: return -1;\r
9952     }\r
9953     return 0;\r
9954 }\r