9cdd4b9c55e8aac1512d3e12f57359376c9fb57c
[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_EditProgs2:\r
5001      LoadEnginePopUp(hwndMain);\r
5002 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
5003       break;\r
5004 \r
5005     case IDM_EditServers:\r
5006       EditTagsPopUp(icsNames, &icsNames);\r
5007       break;\r
5008 \r
5009     case IDM_EditTags:\r
5010     case IDM_Tags:\r
5011       EditTagsProc();\r
5012       break;\r
5013 \r
5014     case IDM_EditBook:\r
5015       EditBookEvent();\r
5016       break;\r
5017 \r
5018     case IDM_EditComment:\r
5019     case IDM_Comment:\r
5020       if (commentUp && editComment) {\r
5021         CommentPopDown();\r
5022       } else {\r
5023         EditCommentEvent();\r
5024       }\r
5025       break;\r
5026 \r
5027     case IDM_Pause:\r
5028       PauseEvent();\r
5029       break;\r
5030 \r
5031     case IDM_Accept:\r
5032       AcceptEvent();\r
5033       break;\r
5034 \r
5035     case IDM_Decline:\r
5036       DeclineEvent();\r
5037       break;\r
5038 \r
5039     case IDM_Rematch:\r
5040       RematchEvent();\r
5041       break;\r
5042 \r
5043     case IDM_CallFlag:\r
5044       CallFlagEvent();\r
5045       break;\r
5046 \r
5047     case IDM_Draw:\r
5048       DrawEvent();\r
5049       break;\r
5050 \r
5051     case IDM_Adjourn:\r
5052       AdjournEvent();\r
5053       break;\r
5054 \r
5055     case IDM_Abort:\r
5056       AbortEvent();\r
5057       break;\r
5058 \r
5059     case IDM_Resign:\r
5060       ResignEvent();\r
5061       break;\r
5062 \r
5063     case IDM_StopObserving:\r
5064       StopObservingEvent();\r
5065       break;\r
5066 \r
5067     case IDM_StopExamining:\r
5068       StopExaminingEvent();\r
5069       break;\r
5070 \r
5071     case IDM_Upload:\r
5072       UploadGameEvent();\r
5073       break;\r
5074 \r
5075     case IDM_TypeInMove:\r
5076       TypeInEvent('\000');\r
5077       break;\r
5078 \r
5079     case IDM_TypeInName:\r
5080       PopUpNameDialog('\000');\r
5081       break;\r
5082 \r
5083     case IDM_Backward:\r
5084       BackwardEvent();\r
5085       SetFocus(hwndMain);\r
5086       break;\r
5087 \r
5088     JAWS_MENU_ITEMS\r
5089 \r
5090     case IDM_Forward:\r
5091       ForwardEvent();\r
5092       SetFocus(hwndMain);\r
5093       break;\r
5094 \r
5095     case IDM_ToStart:\r
5096       ToStartEvent();\r
5097       SetFocus(hwndMain);\r
5098       break;\r
5099 \r
5100     case IDM_ToEnd:\r
5101       ToEndEvent();\r
5102       SetFocus(hwndMain);\r
5103       break;\r
5104 \r
5105     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5106     case OPT_GameListPrev:\r
5107       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5108       break;\r
5109 \r
5110     case IDM_Revert:\r
5111       RevertEvent(FALSE);\r
5112       break;\r
5113 \r
5114     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5115       RevertEvent(TRUE);\r
5116       break;\r
5117 \r
5118     case IDM_TruncateGame:\r
5119       TruncateGameEvent();\r
5120       break;\r
5121 \r
5122     case IDM_MoveNow:\r
5123       MoveNowEvent();\r
5124       break;\r
5125 \r
5126     case IDM_RetractMove:\r
5127       RetractMoveEvent();\r
5128       break;\r
5129 \r
5130     case IDM_FlipView:\r
5131       flipView = !flipView;\r
5132       DrawPosition(FALSE, NULL);\r
5133       break;\r
5134 \r
5135     case IDM_FlipClock:\r
5136       flipClock = !flipClock;\r
5137       DisplayBothClocks();\r
5138       DisplayLogos();\r
5139       break;\r
5140 \r
5141     case IDM_MuteSounds:\r
5142       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5143       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5144                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5145       break;\r
5146 \r
5147     case IDM_GeneralOptions:\r
5148       GeneralOptionsPopup(hwnd);\r
5149       DrawPosition(TRUE, NULL);\r
5150       break;\r
5151 \r
5152     case IDM_BoardOptions:\r
5153       BoardOptionsPopup(hwnd);\r
5154       break;\r
5155 \r
5156     case IDM_EnginePlayOptions:\r
5157       EnginePlayOptionsPopup(hwnd);\r
5158       break;\r
5159 \r
5160     case IDM_Engine1Options:\r
5161       EngineOptionsPopup(hwnd, &first);\r
5162       break;\r
5163 \r
5164     case IDM_Engine2Options:\r
5165       savedHwnd = hwnd;\r
5166       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5167       EngineOptionsPopup(hwnd, &second);\r
5168       break;\r
5169 \r
5170     case IDM_OptionsUCI:\r
5171       UciOptionsPopup(hwnd);\r
5172       break;\r
5173 \r
5174     case IDM_Tourney:\r
5175       TourneyPopup(hwnd);\r
5176       break;\r
5177 \r
5178     case IDM_IcsOptions:\r
5179       IcsOptionsPopup(hwnd);\r
5180       break;\r
5181 \r
5182     case IDM_Fonts:\r
5183       FontsOptionsPopup(hwnd);\r
5184       break;\r
5185 \r
5186     case IDM_Sounds:\r
5187       SoundOptionsPopup(hwnd);\r
5188       break;\r
5189 \r
5190     case IDM_CommPort:\r
5191       CommPortOptionsPopup(hwnd);\r
5192       break;\r
5193 \r
5194     case IDM_LoadOptions:\r
5195       LoadOptionsPopup(hwnd);\r
5196       break;\r
5197 \r
5198     case IDM_SaveOptions:\r
5199       SaveOptionsPopup(hwnd);\r
5200       break;\r
5201 \r
5202     case IDM_TimeControl:\r
5203       TimeControlOptionsPopup(hwnd);\r
5204       break;\r
5205 \r
5206     case IDM_SaveSettings:\r
5207       SaveSettings(settingsFileName);\r
5208       break;\r
5209 \r
5210     case IDM_SaveSettingsOnExit:\r
5211       saveSettingsOnExit = !saveSettingsOnExit;\r
5212       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5213                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5214                                          MF_CHECKED : MF_UNCHECKED));\r
5215       break;\r
5216 \r
5217     case IDM_Hint:\r
5218       HintEvent();\r
5219       break;\r
5220 \r
5221     case IDM_Book:\r
5222       BookEvent();\r
5223       break;\r
5224 \r
5225     case IDM_AboutGame:\r
5226       AboutGameEvent();\r
5227       break;\r
5228 \r
5229     case IDM_Debug:\r
5230       appData.debugMode = !appData.debugMode;\r
5231       if (appData.debugMode) {\r
5232         char dir[MSG_SIZ];\r
5233         GetCurrentDirectory(MSG_SIZ, dir);\r
5234         SetCurrentDirectory(installDir);\r
5235         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5236         SetCurrentDirectory(dir);\r
5237         setbuf(debugFP, NULL);\r
5238       } else {\r
5239         fclose(debugFP);\r
5240         debugFP = NULL;\r
5241       }\r
5242       break;\r
5243 \r
5244     case IDM_HELPCONTENTS:\r
5245       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5246           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5247           MessageBox (GetFocus(),\r
5248                     _("Unable to activate help"),\r
5249                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5250       }\r
5251       break;\r
5252 \r
5253     case IDM_HELPSEARCH:\r
5254         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5255             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5256         MessageBox (GetFocus(),\r
5257                     _("Unable to activate help"),\r
5258                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5259       }\r
5260       break;\r
5261 \r
5262     case IDM_HELPHELP:\r
5263       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5264         MessageBox (GetFocus(),\r
5265                     _("Unable to activate help"),\r
5266                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5267       }\r
5268       break;\r
5269 \r
5270     case IDM_ABOUT:\r
5271       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5272       DialogBox(hInst, \r
5273         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5274         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5275       FreeProcInstance(lpProc);\r
5276       break;\r
5277 \r
5278     case IDM_DirectCommand1:\r
5279       AskQuestionEvent(_("Direct Command"),\r
5280                        _("Send to chess program:"), "", "1");\r
5281       break;\r
5282     case IDM_DirectCommand2:\r
5283       AskQuestionEvent(_("Direct Command"),\r
5284                        _("Send to second chess program:"), "", "2");\r
5285       break;\r
5286 \r
5287     case EP_WhitePawn:\r
5288       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_WhiteKnight:\r
5293       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_WhiteBishop:\r
5298       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_WhiteRook:\r
5303       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_WhiteQueen:\r
5308       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_WhiteFerz:\r
5313       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_WhiteWazir:\r
5318       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_WhiteAlfil:\r
5323       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_WhiteCannon:\r
5328       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_WhiteCardinal:\r
5333       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_WhiteMarshall:\r
5338       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_WhiteKing:\r
5343       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_BlackPawn:\r
5348       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_BlackKnight:\r
5353       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_BlackBishop:\r
5358       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_BlackRook:\r
5363       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_BlackQueen:\r
5368       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_BlackFerz:\r
5373       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case EP_BlackWazir:\r
5378       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case EP_BlackAlfil:\r
5383       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case EP_BlackCannon:\r
5388       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case EP_BlackCardinal:\r
5393       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case EP_BlackMarshall:\r
5398       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case EP_BlackKing:\r
5403       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case EP_EmptySquare:\r
5408       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case EP_ClearBoard:\r
5413       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case EP_White:\r
5418       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5419       fromX = fromY = -1;\r
5420       break;\r
5421 \r
5422     case EP_Black:\r
5423       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5424       fromX = fromY = -1;\r
5425       break;\r
5426 \r
5427     case EP_Promote:\r
5428       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5429       fromX = fromY = -1;\r
5430       break;\r
5431 \r
5432     case EP_Demote:\r
5433       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5434       fromX = fromY = -1;\r
5435       break;\r
5436 \r
5437     case DP_Pawn:\r
5438       DropMenuEvent(WhitePawn, fromX, fromY);\r
5439       fromX = fromY = -1;\r
5440       break;\r
5441 \r
5442     case DP_Knight:\r
5443       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5444       fromX = fromY = -1;\r
5445       break;\r
5446 \r
5447     case DP_Bishop:\r
5448       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5449       fromX = fromY = -1;\r
5450       break;\r
5451 \r
5452     case DP_Rook:\r
5453       DropMenuEvent(WhiteRook, fromX, fromY);\r
5454       fromX = fromY = -1;\r
5455       break;\r
5456 \r
5457     case DP_Queen:\r
5458       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case IDM_English:\r
5463       barbaric = 0; appData.language = "";\r
5464       TranslateMenus(0);\r
5465       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5466       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5467       lastChecked = wmId;\r
5468       break;\r
5469 \r
5470     default:\r
5471       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5472           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5473       else\r
5474       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5475           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5476           TranslateMenus(0);\r
5477           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5478           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5479           lastChecked = wmId;\r
5480           break;\r
5481       }\r
5482       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5483     }\r
5484     break;\r
5485 \r
5486   case WM_TIMER:\r
5487     switch (wParam) {\r
5488     case CLOCK_TIMER_ID:\r
5489       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5490       clockTimerEvent = 0;\r
5491       DecrementClocks(); /* call into back end */\r
5492       break;\r
5493     case LOAD_GAME_TIMER_ID:\r
5494       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5495       loadGameTimerEvent = 0;\r
5496       AutoPlayGameLoop(); /* call into back end */\r
5497       break;\r
5498     case ANALYSIS_TIMER_ID:\r
5499       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5500                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5501         AnalysisPeriodicEvent(0);\r
5502       } else {\r
5503         KillTimer(hwnd, analysisTimerEvent);\r
5504         analysisTimerEvent = 0;\r
5505       }\r
5506       break;\r
5507     case DELAYED_TIMER_ID:\r
5508       KillTimer(hwnd, delayedTimerEvent);\r
5509       delayedTimerEvent = 0;\r
5510       delayedTimerCallback();\r
5511       break;\r
5512     }\r
5513     break;\r
5514 \r
5515   case WM_USER_Input:\r
5516     InputEvent(hwnd, message, wParam, lParam);\r
5517     break;\r
5518 \r
5519   /* [AS] Also move "attached" child windows */\r
5520   case WM_WINDOWPOSCHANGING:\r
5521 \r
5522     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5523         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5524 \r
5525         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5526             /* Window is moving */\r
5527             RECT rcMain;\r
5528 \r
5529 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5530             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5531             rcMain.right  = wpMain.x + wpMain.width;\r
5532             rcMain.top    = wpMain.y;\r
5533             rcMain.bottom = wpMain.y + wpMain.height;\r
5534             \r
5535             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5536             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5537             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5538             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5539             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5540             wpMain.x = lpwp->x;\r
5541             wpMain.y = lpwp->y;\r
5542         }\r
5543     }\r
5544     break;\r
5545 \r
5546   /* [AS] Snapping */\r
5547   case WM_ENTERSIZEMOVE:\r
5548     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5549     if (hwnd == hwndMain) {\r
5550       doingSizing = TRUE;\r
5551       lastSizing = 0;\r
5552     }\r
5553     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5554     break;\r
5555 \r
5556   case WM_SIZING:\r
5557     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5558     if (hwnd == hwndMain) {\r
5559       lastSizing = wParam;\r
5560     }\r
5561     break;\r
5562 \r
5563   case WM_MOVING:\r
5564     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5565       return OnMoving( &sd, hwnd, wParam, lParam );\r
5566 \r
5567   case WM_EXITSIZEMOVE:\r
5568     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5569     if (hwnd == hwndMain) {\r
5570       RECT client;\r
5571       doingSizing = FALSE;\r
5572       InvalidateRect(hwnd, &boardRect, FALSE);\r
5573       GetClientRect(hwnd, &client);\r
5574       ResizeBoard(client.right, client.bottom, lastSizing);\r
5575       lastSizing = 0;\r
5576       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5577     }\r
5578     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5579     break;\r
5580 \r
5581   case WM_DESTROY: /* message: window being destroyed */\r
5582     PostQuitMessage(0);\r
5583     break;\r
5584 \r
5585   case WM_CLOSE:\r
5586     if (hwnd == hwndMain) {\r
5587       ExitEvent(0);\r
5588     }\r
5589     break;\r
5590 \r
5591   default:      /* Passes it on if unprocessed */\r
5592     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5593   }\r
5594   return 0;\r
5595 }\r
5596 \r
5597 /*---------------------------------------------------------------------------*\\r
5598  *\r
5599  * Misc utility routines\r
5600  *\r
5601 \*---------------------------------------------------------------------------*/\r
5602 \r
5603 /*\r
5604  * Decent random number generator, at least not as bad as Windows\r
5605  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5606  */\r
5607 unsigned int randstate;\r
5608 \r
5609 int\r
5610 myrandom(void)\r
5611 {\r
5612   randstate = randstate * 1664525 + 1013904223;\r
5613   return (int) randstate & 0x7fffffff;\r
5614 }\r
5615 \r
5616 void\r
5617 mysrandom(unsigned int seed)\r
5618 {\r
5619   randstate = seed;\r
5620 }\r
5621 \r
5622 \r
5623 /* \r
5624  * returns TRUE if user selects a different color, FALSE otherwise \r
5625  */\r
5626 \r
5627 BOOL\r
5628 ChangeColor(HWND hwnd, COLORREF *which)\r
5629 {\r
5630   static BOOL firstTime = TRUE;\r
5631   static DWORD customColors[16];\r
5632   CHOOSECOLOR cc;\r
5633   COLORREF newcolor;\r
5634   int i;\r
5635   ColorClass ccl;\r
5636 \r
5637   if (firstTime) {\r
5638     /* Make initial colors in use available as custom colors */\r
5639     /* Should we put the compiled-in defaults here instead? */\r
5640     i = 0;\r
5641     customColors[i++] = lightSquareColor & 0xffffff;\r
5642     customColors[i++] = darkSquareColor & 0xffffff;\r
5643     customColors[i++] = whitePieceColor & 0xffffff;\r
5644     customColors[i++] = blackPieceColor & 0xffffff;\r
5645     customColors[i++] = highlightSquareColor & 0xffffff;\r
5646     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5647 \r
5648     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5649       customColors[i++] = textAttribs[ccl].color;\r
5650     }\r
5651     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5652     firstTime = FALSE;\r
5653   }\r
5654 \r
5655   cc.lStructSize = sizeof(cc);\r
5656   cc.hwndOwner = hwnd;\r
5657   cc.hInstance = NULL;\r
5658   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5659   cc.lpCustColors = (LPDWORD) customColors;\r
5660   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5661 \r
5662   if (!ChooseColor(&cc)) return FALSE;\r
5663 \r
5664   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5665   if (newcolor == *which) return FALSE;\r
5666   *which = newcolor;\r
5667   return TRUE;\r
5668 \r
5669   /*\r
5670   InitDrawingColors();\r
5671   InvalidateRect(hwnd, &boardRect, FALSE);\r
5672   */\r
5673 }\r
5674 \r
5675 BOOLEAN\r
5676 MyLoadSound(MySound *ms)\r
5677 {\r
5678   BOOL ok = FALSE;\r
5679   struct stat st;\r
5680   FILE *f;\r
5681 \r
5682   if (ms->data && ms->flag) free(ms->data);\r
5683   ms->data = NULL;\r
5684 \r
5685   switch (ms->name[0]) {\r
5686   case NULLCHAR:\r
5687     /* Silence */\r
5688     ok = TRUE;\r
5689     break;\r
5690   case '$':\r
5691     /* System sound from Control Panel.  Don't preload here. */\r
5692     ok = TRUE;\r
5693     break;\r
5694   case '!':\r
5695     if (ms->name[1] == NULLCHAR) {\r
5696       /* "!" alone = silence */\r
5697       ok = TRUE;\r
5698     } else {\r
5699       /* Builtin wave resource.  Error if not found. */\r
5700       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5701       if (h == NULL) break;\r
5702       ms->data = (void *)LoadResource(hInst, h);\r
5703       ms->flag = 0; // not maloced, so cannot be freed!\r
5704       if (h == NULL) break;\r
5705       ok = TRUE;\r
5706     }\r
5707     break;\r
5708   default:\r
5709     /* .wav file.  Error if not found. */\r
5710     f = fopen(ms->name, "rb");\r
5711     if (f == NULL) break;\r
5712     if (fstat(fileno(f), &st) < 0) break;\r
5713     ms->data = malloc(st.st_size);\r
5714     ms->flag = 1;\r
5715     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5716     fclose(f);\r
5717     ok = TRUE;\r
5718     break;\r
5719   }\r
5720   if (!ok) {\r
5721     char buf[MSG_SIZ];\r
5722       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5723     DisplayError(buf, GetLastError());\r
5724   }\r
5725   return ok;\r
5726 }\r
5727 \r
5728 BOOLEAN\r
5729 MyPlaySound(MySound *ms)\r
5730 {\r
5731   BOOLEAN ok = FALSE;\r
5732 \r
5733   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5734   switch (ms->name[0]) {\r
5735   case NULLCHAR:\r
5736         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5737     /* Silence */\r
5738     ok = TRUE;\r
5739     break;\r
5740   case '$':\r
5741     /* System sound from Control Panel (deprecated feature).\r
5742        "$" alone or an unset sound name gets default beep (still in use). */\r
5743     if (ms->name[1]) {\r
5744       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5745     }\r
5746     if (!ok) ok = MessageBeep(MB_OK);\r
5747     break; \r
5748   case '!':\r
5749     /* Builtin wave resource, or "!" alone for silence */\r
5750     if (ms->name[1]) {\r
5751       if (ms->data == NULL) return FALSE;\r
5752       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5753     } else {\r
5754       ok = TRUE;\r
5755     }\r
5756     break;\r
5757   default:\r
5758     /* .wav file.  Error if not found. */\r
5759     if (ms->data == NULL) return FALSE;\r
5760     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5761     break;\r
5762   }\r
5763   /* Don't print an error: this can happen innocently if the sound driver\r
5764      is busy; for instance, if another instance of WinBoard is playing\r
5765      a sound at about the same time. */\r
5766   return ok;\r
5767 }\r
5768 \r
5769 \r
5770 LRESULT CALLBACK\r
5771 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5772 {\r
5773   BOOL ok;\r
5774   OPENFILENAME *ofn;\r
5775   static UINT *number; /* gross that this is static */\r
5776 \r
5777   switch (message) {\r
5778   case WM_INITDIALOG: /* message: initialize dialog box */\r
5779     /* Center the dialog over the application window */\r
5780     ofn = (OPENFILENAME *) lParam;\r
5781     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5782       number = (UINT *) ofn->lCustData;\r
5783       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5784     } else {\r
5785       number = NULL;\r
5786     }\r
5787     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5788     Translate(hDlg, 1536);\r
5789     return FALSE;  /* Allow for further processing */\r
5790 \r
5791   case WM_COMMAND:\r
5792     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5793       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5794     }\r
5795     return FALSE;  /* Allow for further processing */\r
5796   }\r
5797   return FALSE;\r
5798 }\r
5799 \r
5800 UINT APIENTRY\r
5801 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5802 {\r
5803   static UINT *number;\r
5804   OPENFILENAME *ofname;\r
5805   OFNOTIFY *ofnot;\r
5806   switch (uiMsg) {\r
5807   case WM_INITDIALOG:\r
5808     Translate(hdlg, DLG_IndexNumber);\r
5809     ofname = (OPENFILENAME *)lParam;\r
5810     number = (UINT *)(ofname->lCustData);\r
5811     break;\r
5812   case WM_NOTIFY:\r
5813     ofnot = (OFNOTIFY *)lParam;\r
5814     if (ofnot->hdr.code == CDN_FILEOK) {\r
5815       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5816     }\r
5817     break;\r
5818   }\r
5819   return 0;\r
5820 }\r
5821 \r
5822 \r
5823 FILE *\r
5824 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5825                char *nameFilt, char *dlgTitle, UINT *number,\r
5826                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5827 {\r
5828   OPENFILENAME openFileName;\r
5829   char buf1[MSG_SIZ];\r
5830   FILE *f;\r
5831 \r
5832   if (fileName == NULL) fileName = buf1;\r
5833   if (defName == NULL) {\r
5834     safeStrCpy(fileName, "*.", 3 );\r
5835     strcat(fileName, defExt);\r
5836   } else {\r
5837     safeStrCpy(fileName, defName, MSG_SIZ );\r
5838   }\r
5839     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5840   if (number) *number = 0;\r
5841 \r
5842   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5843   openFileName.hwndOwner         = hwnd;\r
5844   openFileName.hInstance         = (HANDLE) hInst;\r
5845   openFileName.lpstrFilter       = nameFilt;\r
5846   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5847   openFileName.nMaxCustFilter    = 0L;\r
5848   openFileName.nFilterIndex      = 1L;\r
5849   openFileName.lpstrFile         = fileName;\r
5850   openFileName.nMaxFile          = MSG_SIZ;\r
5851   openFileName.lpstrFileTitle    = fileTitle;\r
5852   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5853   openFileName.lpstrInitialDir   = NULL;\r
5854   openFileName.lpstrTitle        = dlgTitle;\r
5855   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5856     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5857     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5858     | (oldDialog ? 0 : OFN_EXPLORER);\r
5859   openFileName.nFileOffset       = 0;\r
5860   openFileName.nFileExtension    = 0;\r
5861   openFileName.lpstrDefExt       = defExt;\r
5862   openFileName.lCustData         = (LONG) number;\r
5863   openFileName.lpfnHook          = oldDialog ?\r
5864     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5865   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5866 \r
5867   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5868                         GetOpenFileName(&openFileName)) {\r
5869     /* open the file */\r
5870     f = fopen(openFileName.lpstrFile, write);\r
5871     if (f == NULL) {\r
5872       MessageBox(hwnd, _("File open failed"), NULL,\r
5873                  MB_OK|MB_ICONEXCLAMATION);\r
5874       return NULL;\r
5875     }\r
5876   } else {\r
5877     int err = CommDlgExtendedError();\r
5878     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5879     return FALSE;\r
5880   }\r
5881   return f;\r
5882 }\r
5883 \r
5884 \r
5885 \r
5886 VOID APIENTRY\r
5887 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5888 {\r
5889   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5890 \r
5891   /*\r
5892    * Get the first pop-up menu in the menu template. This is the\r
5893    * menu that TrackPopupMenu displays.\r
5894    */\r
5895   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5896   TranslateOneMenu(10, hmenuTrackPopup);\r
5897 \r
5898   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5899 \r
5900   /*\r
5901    * TrackPopup uses screen coordinates, so convert the\r
5902    * coordinates of the mouse click to screen coordinates.\r
5903    */\r
5904   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5905 \r
5906   /* Draw and track the floating pop-up menu. */\r
5907   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5908                  pt.x, pt.y, 0, hwnd, NULL);\r
5909 \r
5910   /* Destroy the menu.*/\r
5911   DestroyMenu(hmenu);\r
5912 }\r
5913    \r
5914 typedef struct {\r
5915   HWND hDlg, hText;\r
5916   int sizeX, sizeY, newSizeX, newSizeY;\r
5917   HDWP hdwp;\r
5918 } ResizeEditPlusButtonsClosure;\r
5919 \r
5920 BOOL CALLBACK\r
5921 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5922 {\r
5923   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5924   RECT rect;\r
5925   POINT pt;\r
5926 \r
5927   if (hChild == cl->hText) return TRUE;\r
5928   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5929   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5930   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5931   ScreenToClient(cl->hDlg, &pt);\r
5932   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5933     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5934   return TRUE;\r
5935 }\r
5936 \r
5937 /* Resize a dialog that has a (rich) edit field filling most of\r
5938    the top, with a row of buttons below */\r
5939 VOID\r
5940 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5941 {\r
5942   RECT rectText;\r
5943   int newTextHeight, newTextWidth;\r
5944   ResizeEditPlusButtonsClosure cl;\r
5945   \r
5946   /*if (IsIconic(hDlg)) return;*/\r
5947   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5948   \r
5949   cl.hdwp = BeginDeferWindowPos(8);\r
5950 \r
5951   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5952   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5953   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5954   if (newTextHeight < 0) {\r
5955     newSizeY += -newTextHeight;\r
5956     newTextHeight = 0;\r
5957   }\r
5958   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5959     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5960 \r
5961   cl.hDlg = hDlg;\r
5962   cl.hText = hText;\r
5963   cl.sizeX = sizeX;\r
5964   cl.sizeY = sizeY;\r
5965   cl.newSizeX = newSizeX;\r
5966   cl.newSizeY = newSizeY;\r
5967   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5968 \r
5969   EndDeferWindowPos(cl.hdwp);\r
5970 }\r
5971 \r
5972 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5973 {\r
5974     RECT    rChild, rParent;\r
5975     int     wChild, hChild, wParent, hParent;\r
5976     int     wScreen, hScreen, xNew, yNew;\r
5977     HDC     hdc;\r
5978 \r
5979     /* Get the Height and Width of the child window */\r
5980     GetWindowRect (hwndChild, &rChild);\r
5981     wChild = rChild.right - rChild.left;\r
5982     hChild = rChild.bottom - rChild.top;\r
5983 \r
5984     /* Get the Height and Width of the parent window */\r
5985     GetWindowRect (hwndParent, &rParent);\r
5986     wParent = rParent.right - rParent.left;\r
5987     hParent = rParent.bottom - rParent.top;\r
5988 \r
5989     /* Get the display limits */\r
5990     hdc = GetDC (hwndChild);\r
5991     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5992     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5993     ReleaseDC(hwndChild, hdc);\r
5994 \r
5995     /* Calculate new X position, then adjust for screen */\r
5996     xNew = rParent.left + ((wParent - wChild) /2);\r
5997     if (xNew < 0) {\r
5998         xNew = 0;\r
5999     } else if ((xNew+wChild) > wScreen) {\r
6000         xNew = wScreen - wChild;\r
6001     }\r
6002 \r
6003     /* Calculate new Y position, then adjust for screen */\r
6004     if( mode == 0 ) {\r
6005         yNew = rParent.top  + ((hParent - hChild) /2);\r
6006     }\r
6007     else {\r
6008         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6009     }\r
6010 \r
6011     if (yNew < 0) {\r
6012         yNew = 0;\r
6013     } else if ((yNew+hChild) > hScreen) {\r
6014         yNew = hScreen - hChild;\r
6015     }\r
6016 \r
6017     /* Set it, and return */\r
6018     return SetWindowPos (hwndChild, NULL,\r
6019                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6020 }\r
6021 \r
6022 /* Center one window over another */\r
6023 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6024 {\r
6025     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6026 }\r
6027 \r
6028 /*---------------------------------------------------------------------------*\\r
6029  *\r
6030  * Startup Dialog functions\r
6031  *\r
6032 \*---------------------------------------------------------------------------*/\r
6033 void\r
6034 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6035 {\r
6036   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6037 \r
6038   while (*cd != NULL) {\r
6039     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6040     cd++;\r
6041   }\r
6042 }\r
6043 \r
6044 void\r
6045 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6046 {\r
6047   char buf1[MAX_ARG_LEN];\r
6048   int len;\r
6049 \r
6050   if (str[0] == '@') {\r
6051     FILE* f = fopen(str + 1, "r");\r
6052     if (f == NULL) {\r
6053       DisplayFatalError(str + 1, errno, 2);\r
6054       return;\r
6055     }\r
6056     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6057     fclose(f);\r
6058     buf1[len] = NULLCHAR;\r
6059     str = buf1;\r
6060   }\r
6061 \r
6062   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6063 \r
6064   for (;;) {\r
6065     char buf[MSG_SIZ];\r
6066     char *end = strchr(str, '\n');\r
6067     if (end == NULL) return;\r
6068     memcpy(buf, str, end - str);\r
6069     buf[end - str] = NULLCHAR;\r
6070     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6071     str = end + 1;\r
6072   }\r
6073 }\r
6074 \r
6075 void\r
6076 SetStartupDialogEnables(HWND hDlg)\r
6077 {\r
6078   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6079     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6080     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6081   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6082     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6083   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6084     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6085   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6086     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6087   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6088     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6089     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6090     IsDlgButtonChecked(hDlg, OPT_View));\r
6091 }\r
6092 \r
6093 char *\r
6094 QuoteForFilename(char *filename)\r
6095 {\r
6096   int dquote, space;\r
6097   dquote = strchr(filename, '"') != NULL;\r
6098   space = strchr(filename, ' ') != NULL;\r
6099   if (dquote || space) {\r
6100     if (dquote) {\r
6101       return "'";\r
6102     } else {\r
6103       return "\"";\r
6104     }\r
6105   } else {\r
6106     return "";\r
6107   }\r
6108 }\r
6109 \r
6110 VOID\r
6111 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6112 {\r
6113   char buf[MSG_SIZ];\r
6114   char *q;\r
6115 \r
6116   InitComboStringsFromOption(hwndCombo, nthnames);\r
6117   q = QuoteForFilename(nthcp);\r
6118     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6119   if (*nthdir != NULLCHAR) {\r
6120     q = QuoteForFilename(nthdir);\r
6121       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6122   }\r
6123   if (*nthcp == NULLCHAR) {\r
6124     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6125   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6126     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6127     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6128   }\r
6129 }\r
6130 \r
6131 LRESULT CALLBACK\r
6132 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6133 {\r
6134   char buf[MSG_SIZ];\r
6135   HANDLE hwndCombo;\r
6136   char *p;\r
6137 \r
6138   switch (message) {\r
6139   case WM_INITDIALOG:\r
6140     /* Center the dialog */\r
6141     CenterWindow (hDlg, GetDesktopWindow());\r
6142     Translate(hDlg, DLG_Startup);\r
6143     /* Initialize the dialog items */\r
6144     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6145                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6146                   firstChessProgramNames);\r
6147     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6148                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6149                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6150     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6151     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6152       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6153     if (*appData.icsHelper != NULLCHAR) {\r
6154       char *q = QuoteForFilename(appData.icsHelper);\r
6155       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6156     }\r
6157     if (*appData.icsHost == NULLCHAR) {\r
6158       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6159       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6160     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6161       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6162       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6163     }\r
6164 \r
6165     if (appData.icsActive) {\r
6166       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6167     }\r
6168     else if (appData.noChessProgram) {\r
6169       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6170     }\r
6171     else {\r
6172       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6173     }\r
6174 \r
6175     SetStartupDialogEnables(hDlg);\r
6176     return TRUE;\r
6177 \r
6178   case WM_COMMAND:\r
6179     switch (LOWORD(wParam)) {\r
6180     case IDOK:\r
6181       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6182         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6183         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6184         p = buf;\r
6185         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6186         ParseArgs(StringGet, &p);\r
6187         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6188         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6189         p = buf;\r
6190         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6191         ParseArgs(StringGet, &p);\r
6192         SwapEngines(singleList); // ... and then make it 'second'\r
6193         appData.noChessProgram = FALSE;\r
6194         appData.icsActive = FALSE;\r
6195       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6196         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6197         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6198         p = buf;\r
6199         ParseArgs(StringGet, &p);\r
6200         if (appData.zippyPlay) {\r
6201           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6202           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6203           p = buf;\r
6204           ParseArgs(StringGet, &p);\r
6205         }\r
6206       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6207         appData.noChessProgram = TRUE;\r
6208         appData.icsActive = FALSE;\r
6209       } else {\r
6210         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6211                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6212         return TRUE;\r
6213       }\r
6214       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6215         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6216         p = buf;\r
6217         ParseArgs(StringGet, &p);\r
6218       }\r
6219       EndDialog(hDlg, TRUE);\r
6220       return TRUE;\r
6221 \r
6222     case IDCANCEL:\r
6223       ExitEvent(0);\r
6224       return TRUE;\r
6225 \r
6226     case IDM_HELPCONTENTS:\r
6227       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6228         MessageBox (GetFocus(),\r
6229                     _("Unable to activate help"),\r
6230                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6231       }\r
6232       break;\r
6233 \r
6234     default:\r
6235       SetStartupDialogEnables(hDlg);\r
6236       break;\r
6237     }\r
6238     break;\r
6239   }\r
6240   return FALSE;\r
6241 }\r
6242 \r
6243 /*---------------------------------------------------------------------------*\\r
6244  *\r
6245  * About box dialog functions\r
6246  *\r
6247 \*---------------------------------------------------------------------------*/\r
6248 \r
6249 /* Process messages for "About" dialog box */\r
6250 LRESULT CALLBACK\r
6251 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6252 {\r
6253   switch (message) {\r
6254   case WM_INITDIALOG: /* message: initialize dialog box */\r
6255     /* Center the dialog over the application window */\r
6256     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6257     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6258     Translate(hDlg, ABOUTBOX);\r
6259     JAWS_COPYRIGHT\r
6260     return (TRUE);\r
6261 \r
6262   case WM_COMMAND: /* message: received a command */\r
6263     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6264         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6265       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6266       return (TRUE);\r
6267     }\r
6268     break;\r
6269   }\r
6270   return (FALSE);\r
6271 }\r
6272 \r
6273 /*---------------------------------------------------------------------------*\\r
6274  *\r
6275  * Comment Dialog functions\r
6276  *\r
6277 \*---------------------------------------------------------------------------*/\r
6278 \r
6279 LRESULT CALLBACK\r
6280 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6281 {\r
6282   static HANDLE hwndText = NULL;\r
6283   int len, newSizeX, newSizeY, flags;\r
6284   static int sizeX, sizeY;\r
6285   char *str;\r
6286   RECT rect;\r
6287   MINMAXINFO *mmi;\r
6288 \r
6289   switch (message) {\r
6290   case WM_INITDIALOG: /* message: initialize dialog box */\r
6291     /* Initialize the dialog items */\r
6292     Translate(hDlg, DLG_EditComment);\r
6293     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6294     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6295     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6296     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6297     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6298     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6299     SetWindowText(hDlg, commentTitle);\r
6300     if (editComment) {\r
6301       SetFocus(hwndText);\r
6302     } else {\r
6303       SetFocus(GetDlgItem(hDlg, IDOK));\r
6304     }\r
6305     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6306                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6307                 MAKELPARAM(FALSE, 0));\r
6308     /* Size and position the dialog */\r
6309     if (!commentDialog) {\r
6310       commentDialog = hDlg;\r
6311       flags = SWP_NOZORDER;\r
6312       GetClientRect(hDlg, &rect);\r
6313       sizeX = rect.right;\r
6314       sizeY = rect.bottom;\r
6315       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6316           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6317         WINDOWPLACEMENT wp;\r
6318         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6319         wp.length = sizeof(WINDOWPLACEMENT);\r
6320         wp.flags = 0;\r
6321         wp.showCmd = SW_SHOW;\r
6322         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6323         wp.rcNormalPosition.left = wpComment.x;\r
6324         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6325         wp.rcNormalPosition.top = wpComment.y;\r
6326         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6327         SetWindowPlacement(hDlg, &wp);\r
6328 \r
6329         GetClientRect(hDlg, &rect);\r
6330         newSizeX = rect.right;\r
6331         newSizeY = rect.bottom;\r
6332         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6333                               newSizeX, newSizeY);\r
6334         sizeX = newSizeX;\r
6335         sizeY = newSizeY;\r
6336       }\r
6337     }\r
6338     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6339     return FALSE;\r
6340 \r
6341   case WM_COMMAND: /* message: received a command */\r
6342     switch (LOWORD(wParam)) {\r
6343     case IDOK:\r
6344       if (editComment) {\r
6345         char *p, *q;\r
6346         /* Read changed options from the dialog box */\r
6347         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6348         len = GetWindowTextLength(hwndText);\r
6349         str = (char *) malloc(len + 1);\r
6350         GetWindowText(hwndText, str, len + 1);\r
6351         p = q = str;\r
6352         while (*q) {\r
6353           if (*q == '\r')\r
6354             q++;\r
6355           else\r
6356             *p++ = *q++;\r
6357         }\r
6358         *p = NULLCHAR;\r
6359         ReplaceComment(commentIndex, str);\r
6360         free(str);\r
6361       }\r
6362       CommentPopDown();\r
6363       return TRUE;\r
6364 \r
6365     case IDCANCEL:\r
6366     case OPT_CancelComment:\r
6367       CommentPopDown();\r
6368       return TRUE;\r
6369 \r
6370     case OPT_ClearComment:\r
6371       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6372       break;\r
6373 \r
6374     case OPT_EditComment:\r
6375       EditCommentEvent();\r
6376       return TRUE;\r
6377 \r
6378     default:\r
6379       break;\r
6380     }\r
6381     break;\r
6382 \r
6383   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6384         if( wParam == OPT_CommentText ) {\r
6385             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6386 \r
6387             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6388                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6389                 POINTL pt;\r
6390                 LRESULT index;\r
6391 \r
6392                 pt.x = LOWORD( lpMF->lParam );\r
6393                 pt.y = HIWORD( lpMF->lParam );\r
6394 \r
6395                 if(lpMF->msg == WM_CHAR) {\r
6396                         CHARRANGE sel;\r
6397                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6398                         index = sel.cpMin;\r
6399                 } else\r
6400                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6401 \r
6402                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6403                 len = GetWindowTextLength(hwndText);\r
6404                 str = (char *) malloc(len + 1);\r
6405                 GetWindowText(hwndText, str, len + 1);\r
6406                 ReplaceComment(commentIndex, str);\r
6407                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6408                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6409                 free(str);\r
6410 \r
6411                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6412                 lpMF->msg = WM_USER;\r
6413 \r
6414                 return TRUE;\r
6415             }\r
6416         }\r
6417         break;\r
6418 \r
6419   case WM_SIZE:\r
6420     newSizeX = LOWORD(lParam);\r
6421     newSizeY = HIWORD(lParam);\r
6422     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6423     sizeX = newSizeX;\r
6424     sizeY = newSizeY;\r
6425     break;\r
6426 \r
6427   case WM_GETMINMAXINFO:\r
6428     /* Prevent resizing window too small */\r
6429     mmi = (MINMAXINFO *) lParam;\r
6430     mmi->ptMinTrackSize.x = 100;\r
6431     mmi->ptMinTrackSize.y = 100;\r
6432     break;\r
6433   }\r
6434   return FALSE;\r
6435 }\r
6436 \r
6437 VOID\r
6438 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6439 {\r
6440   FARPROC lpProc;\r
6441   char *p, *q;\r
6442 \r
6443   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6444 \r
6445   if (str == NULL) str = "";\r
6446   p = (char *) malloc(2 * strlen(str) + 2);\r
6447   q = p;\r
6448   while (*str) {\r
6449     if (*str == '\n') *q++ = '\r';\r
6450     *q++ = *str++;\r
6451   }\r
6452   *q = NULLCHAR;\r
6453   if (commentText != NULL) free(commentText);\r
6454 \r
6455   commentIndex = index;\r
6456   commentTitle = title;\r
6457   commentText = p;\r
6458   editComment = edit;\r
6459 \r
6460   if (commentDialog) {\r
6461     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6462     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6463   } else {\r
6464     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6465     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6466                  hwndMain, (DLGPROC)lpProc);\r
6467     FreeProcInstance(lpProc);\r
6468   }\r
6469   commentUp = TRUE;\r
6470 }\r
6471 \r
6472 \r
6473 /*---------------------------------------------------------------------------*\\r
6474  *\r
6475  * Type-in move dialog functions\r
6476  * \r
6477 \*---------------------------------------------------------------------------*/\r
6478 \r
6479 LRESULT CALLBACK\r
6480 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6481 {\r
6482   char move[MSG_SIZ];\r
6483   HWND hInput;\r
6484 \r
6485   switch (message) {\r
6486   case WM_INITDIALOG:\r
6487     move[0] = (char) lParam;\r
6488     move[1] = NULLCHAR;\r
6489     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6490     Translate(hDlg, DLG_TypeInMove);\r
6491     hInput = GetDlgItem(hDlg, OPT_Move);\r
6492     SetWindowText(hInput, move);\r
6493     SetFocus(hInput);\r
6494     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6495     return FALSE;\r
6496 \r
6497   case WM_COMMAND:\r
6498     switch (LOWORD(wParam)) {\r
6499     case IDOK:\r
6500 \r
6501       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6502       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6503       TypeInDoneEvent(move);\r
6504       EndDialog(hDlg, TRUE);\r
6505       return TRUE;\r
6506     case IDCANCEL:\r
6507       EndDialog(hDlg, FALSE);\r
6508       return TRUE;\r
6509     default:\r
6510       break;\r
6511     }\r
6512     break;\r
6513   }\r
6514   return FALSE;\r
6515 }\r
6516 \r
6517 VOID\r
6518 PopUpMoveDialog(char firstchar)\r
6519 {\r
6520     FARPROC lpProc;\r
6521 \r
6522       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6523       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6524         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6525       FreeProcInstance(lpProc);\r
6526 }\r
6527 \r
6528 /*---------------------------------------------------------------------------*\\r
6529  *\r
6530  * Type-in name dialog functions\r
6531  * \r
6532 \*---------------------------------------------------------------------------*/\r
6533 \r
6534 LRESULT CALLBACK\r
6535 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6536 {\r
6537   char move[MSG_SIZ];\r
6538   HWND hInput;\r
6539 \r
6540   switch (message) {\r
6541   case WM_INITDIALOG:\r
6542     move[0] = (char) lParam;\r
6543     move[1] = NULLCHAR;\r
6544     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6545     Translate(hDlg, DLG_TypeInName);\r
6546     hInput = GetDlgItem(hDlg, OPT_Name);\r
6547     SetWindowText(hInput, move);\r
6548     SetFocus(hInput);\r
6549     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6550     return FALSE;\r
6551 \r
6552   case WM_COMMAND:\r
6553     switch (LOWORD(wParam)) {\r
6554     case IDOK:\r
6555       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6556       appData.userName = strdup(move);\r
6557       SetUserLogo();\r
6558       SetGameInfo();\r
6559       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6560         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6561         DisplayTitle(move);\r
6562       }\r
6563 \r
6564 \r
6565       EndDialog(hDlg, TRUE);\r
6566       return TRUE;\r
6567     case IDCANCEL:\r
6568       EndDialog(hDlg, FALSE);\r
6569       return TRUE;\r
6570     default:\r
6571       break;\r
6572     }\r
6573     break;\r
6574   }\r
6575   return FALSE;\r
6576 }\r
6577 \r
6578 VOID\r
6579 PopUpNameDialog(char firstchar)\r
6580 {\r
6581     FARPROC lpProc;\r
6582     \r
6583       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6584       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6585         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6586       FreeProcInstance(lpProc);\r
6587 }\r
6588 \r
6589 /*---------------------------------------------------------------------------*\\r
6590  *\r
6591  *  Error dialogs\r
6592  * \r
6593 \*---------------------------------------------------------------------------*/\r
6594 \r
6595 /* Nonmodal error box */\r
6596 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6597                              WPARAM wParam, LPARAM lParam);\r
6598 \r
6599 VOID\r
6600 ErrorPopUp(char *title, char *content)\r
6601 {\r
6602   FARPROC lpProc;\r
6603   char *p, *q;\r
6604   BOOLEAN modal = hwndMain == NULL;\r
6605 \r
6606   p = content;\r
6607   q = errorMessage;\r
6608   while (*p) {\r
6609     if (*p == '\n') {\r
6610       if (modal) {\r
6611         *q++ = ' ';\r
6612         p++;\r
6613       } else {\r
6614         *q++ = '\r';\r
6615         *q++ = *p++;\r
6616       }\r
6617     } else {\r
6618       *q++ = *p++;\r
6619     }\r
6620   }\r
6621   *q = NULLCHAR;\r
6622   strncpy(errorTitle, title, sizeof(errorTitle));\r
6623   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6624   \r
6625   if (modal) {\r
6626     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6627   } else {\r
6628     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6629     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6630                  hwndMain, (DLGPROC)lpProc);\r
6631     FreeProcInstance(lpProc);\r
6632   }\r
6633 }\r
6634 \r
6635 VOID\r
6636 ErrorPopDown()\r
6637 {\r
6638   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6639   if (errorDialog == NULL) return;\r
6640   DestroyWindow(errorDialog);\r
6641   errorDialog = NULL;\r
6642   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6643 }\r
6644 \r
6645 LRESULT CALLBACK\r
6646 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6647 {\r
6648   HANDLE hwndText;\r
6649   RECT rChild;\r
6650 \r
6651   switch (message) {\r
6652   case WM_INITDIALOG:\r
6653     GetWindowRect(hDlg, &rChild);\r
6654 \r
6655     /*\r
6656     SetWindowPos(hDlg, NULL, rChild.left,\r
6657       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6658       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6659     */\r
6660 \r
6661     /* \r
6662         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6663         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6664         and it doesn't work when you resize the dialog.\r
6665         For now, just give it a default position.\r
6666     */\r
6667     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6668     Translate(hDlg, DLG_Error);\r
6669 \r
6670     errorDialog = hDlg;\r
6671     SetWindowText(hDlg, errorTitle);\r
6672     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6673     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6674     return FALSE;\r
6675 \r
6676   case WM_COMMAND:\r
6677     switch (LOWORD(wParam)) {\r
6678     case IDOK:\r
6679     case IDCANCEL:\r
6680       if (errorDialog == hDlg) errorDialog = NULL;\r
6681       DestroyWindow(hDlg);\r
6682       return TRUE;\r
6683 \r
6684     default:\r
6685       break;\r
6686     }\r
6687     break;\r
6688   }\r
6689   return FALSE;\r
6690 }\r
6691 \r
6692 #ifdef GOTHIC\r
6693 HWND gothicDialog = NULL;\r
6694 \r
6695 LRESULT CALLBACK\r
6696 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6697 {\r
6698   HANDLE hwndText;\r
6699   RECT rChild;\r
6700   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6701 \r
6702   switch (message) {\r
6703   case WM_INITDIALOG:\r
6704     GetWindowRect(hDlg, &rChild);\r
6705 \r
6706     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6707                                                              SWP_NOZORDER);\r
6708 \r
6709     /* \r
6710         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6711         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6712         and it doesn't work when you resize the dialog.\r
6713         For now, just give it a default position.\r
6714     */\r
6715     gothicDialog = hDlg;\r
6716     SetWindowText(hDlg, errorTitle);\r
6717     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6718     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6719     return FALSE;\r
6720 \r
6721   case WM_COMMAND:\r
6722     switch (LOWORD(wParam)) {\r
6723     case IDOK:\r
6724     case IDCANCEL:\r
6725       if (errorDialog == hDlg) errorDialog = NULL;\r
6726       DestroyWindow(hDlg);\r
6727       return TRUE;\r
6728 \r
6729     default:\r
6730       break;\r
6731     }\r
6732     break;\r
6733   }\r
6734   return FALSE;\r
6735 }\r
6736 \r
6737 VOID\r
6738 GothicPopUp(char *title, VariantClass variant)\r
6739 {\r
6740   FARPROC lpProc;\r
6741   static char *lastTitle;\r
6742 \r
6743   strncpy(errorTitle, title, sizeof(errorTitle));\r
6744   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6745 \r
6746   if(lastTitle != title && gothicDialog != NULL) {\r
6747     DestroyWindow(gothicDialog);\r
6748     gothicDialog = NULL;\r
6749   }\r
6750   if(variant != VariantNormal && gothicDialog == NULL) {\r
6751     title = lastTitle;\r
6752     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6753     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6754                  hwndMain, (DLGPROC)lpProc);\r
6755     FreeProcInstance(lpProc);\r
6756   }\r
6757 }\r
6758 #endif\r
6759 \r
6760 /*---------------------------------------------------------------------------*\\r
6761  *\r
6762  *  Ics Interaction console functions\r
6763  *\r
6764 \*---------------------------------------------------------------------------*/\r
6765 \r
6766 #define HISTORY_SIZE 64\r
6767 static char *history[HISTORY_SIZE];\r
6768 int histIn = 0, histP = 0;\r
6769 \r
6770 VOID\r
6771 SaveInHistory(char *cmd)\r
6772 {\r
6773   if (history[histIn] != NULL) {\r
6774     free(history[histIn]);\r
6775     history[histIn] = NULL;\r
6776   }\r
6777   if (*cmd == NULLCHAR) return;\r
6778   history[histIn] = StrSave(cmd);\r
6779   histIn = (histIn + 1) % HISTORY_SIZE;\r
6780   if (history[histIn] != NULL) {\r
6781     free(history[histIn]);\r
6782     history[histIn] = NULL;\r
6783   }\r
6784   histP = histIn;\r
6785 }\r
6786 \r
6787 char *\r
6788 PrevInHistory(char *cmd)\r
6789 {\r
6790   int newhp;\r
6791   if (histP == histIn) {\r
6792     if (history[histIn] != NULL) free(history[histIn]);\r
6793     history[histIn] = StrSave(cmd);\r
6794   }\r
6795   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6796   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6797   histP = newhp;\r
6798   return history[histP];\r
6799 }\r
6800 \r
6801 char *\r
6802 NextInHistory()\r
6803 {\r
6804   if (histP == histIn) return NULL;\r
6805   histP = (histP + 1) % HISTORY_SIZE;\r
6806   return history[histP];   \r
6807 }\r
6808 \r
6809 HMENU\r
6810 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6811 {\r
6812   HMENU hmenu, h;\r
6813   int i = 0;\r
6814   hmenu = LoadMenu(hInst, "TextMenu");\r
6815   h = GetSubMenu(hmenu, 0);\r
6816   while (e->item) {\r
6817     if (strcmp(e->item, "-") == 0) {\r
6818       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6819     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6820       int flags = MF_STRING, j = 0;\r
6821       if (e->item[0] == '|') {\r
6822         flags |= MF_MENUBARBREAK;\r
6823         j++;\r
6824       }\r
6825       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6826       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6827     }\r
6828     e++;\r
6829     i++;\r
6830   } \r
6831   return hmenu;\r
6832 }\r
6833 \r
6834 WNDPROC consoleTextWindowProc;\r
6835 \r
6836 void\r
6837 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6838 {\r
6839   char buf[MSG_SIZ], name[MSG_SIZ];\r
6840   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6841   CHARRANGE sel;\r
6842 \r
6843   if (!getname) {\r
6844     SetWindowText(hInput, command);\r
6845     if (immediate) {\r
6846       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6847     } else {\r
6848       sel.cpMin = 999999;\r
6849       sel.cpMax = 999999;\r
6850       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6851       SetFocus(hInput);\r
6852     }\r
6853     return;\r
6854   }    \r
6855   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6856   if (sel.cpMin == sel.cpMax) {\r
6857     /* Expand to surrounding word */\r
6858     TEXTRANGE tr;\r
6859     do {\r
6860       tr.chrg.cpMax = sel.cpMin;\r
6861       tr.chrg.cpMin = --sel.cpMin;\r
6862       if (sel.cpMin < 0) break;\r
6863       tr.lpstrText = name;\r
6864       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6865     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6866     sel.cpMin++;\r
6867 \r
6868     do {\r
6869       tr.chrg.cpMin = sel.cpMax;\r
6870       tr.chrg.cpMax = ++sel.cpMax;\r
6871       tr.lpstrText = name;\r
6872       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6873     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6874     sel.cpMax--;\r
6875 \r
6876     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6877       MessageBeep(MB_ICONEXCLAMATION);\r
6878       return;\r
6879     }\r
6880     tr.chrg = sel;\r
6881     tr.lpstrText = name;\r
6882     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6883   } else {\r
6884     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6885       MessageBeep(MB_ICONEXCLAMATION);\r
6886       return;\r
6887     }\r
6888     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6889   }\r
6890   if (immediate) {\r
6891     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6892     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6893     SetWindowText(hInput, buf);\r
6894     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6895   } else {\r
6896     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6897       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6898     SetWindowText(hInput, buf);\r
6899     sel.cpMin = 999999;\r
6900     sel.cpMax = 999999;\r
6901     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6902     SetFocus(hInput);\r
6903   }\r
6904 }\r
6905 \r
6906 LRESULT CALLBACK \r
6907 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6908 {\r
6909   HWND hInput;\r
6910   CHARRANGE sel;\r
6911 \r
6912   switch (message) {\r
6913   case WM_KEYDOWN:\r
6914     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6915     if(wParam=='R') return 0;\r
6916     switch (wParam) {\r
6917     case VK_PRIOR:\r
6918       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6919       return 0;\r
6920     case VK_NEXT:\r
6921       sel.cpMin = 999999;\r
6922       sel.cpMax = 999999;\r
6923       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6924       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6925       return 0;\r
6926     }\r
6927     break;\r
6928   case WM_CHAR:\r
6929    if(wParam != '\022') {\r
6930     if (wParam == '\t') {\r
6931       if (GetKeyState(VK_SHIFT) < 0) {\r
6932         /* shifted */\r
6933         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6934         if (buttonDesc[0].hwnd) {\r
6935           SetFocus(buttonDesc[0].hwnd);\r
6936         } else {\r
6937           SetFocus(hwndMain);\r
6938         }\r
6939       } else {\r
6940         /* unshifted */\r
6941         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6942       }\r
6943     } else {\r
6944       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6945       JAWS_DELETE( SetFocus(hInput); )\r
6946       SendMessage(hInput, message, wParam, lParam);\r
6947     }\r
6948     return 0;\r
6949    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6950    lParam = -1;\r
6951   case WM_RBUTTONDOWN:\r
6952     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6953       /* Move selection here if it was empty */\r
6954       POINT pt;\r
6955       pt.x = LOWORD(lParam);\r
6956       pt.y = HIWORD(lParam);\r
6957       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6958       if (sel.cpMin == sel.cpMax) {\r
6959         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6960         sel.cpMax = sel.cpMin;\r
6961         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6962       }\r
6963       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6964 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6965       POINT pt;\r
6966       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6967       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6968       if (sel.cpMin == sel.cpMax) {\r
6969         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6970         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6971       }\r
6972       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6973         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6974       }\r
6975       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6976       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6977       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6978       MenuPopup(hwnd, pt, hmenu, -1);\r
6979 }\r
6980     }\r
6981     return 0;\r
6982   case WM_RBUTTONUP:\r
6983     if (GetKeyState(VK_SHIFT) & ~1) {\r
6984       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6985         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6986     }\r
6987     return 0;\r
6988   case WM_PASTE:\r
6989     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6990     SetFocus(hInput);\r
6991     return SendMessage(hInput, message, wParam, lParam);\r
6992   case WM_MBUTTONDOWN:\r
6993     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6994   case WM_COMMAND:\r
6995     switch (LOWORD(wParam)) {\r
6996     case IDM_QuickPaste:\r
6997       {\r
6998         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6999         if (sel.cpMin == sel.cpMax) {\r
7000           MessageBeep(MB_ICONEXCLAMATION);\r
7001           return 0;\r
7002         }\r
7003         SendMessage(hwnd, WM_COPY, 0, 0);\r
7004         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7005         SendMessage(hInput, WM_PASTE, 0, 0);\r
7006         SetFocus(hInput);\r
7007         return 0;\r
7008       }\r
7009     case IDM_Cut:\r
7010       SendMessage(hwnd, WM_CUT, 0, 0);\r
7011       return 0;\r
7012     case IDM_Paste:\r
7013       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7014       return 0;\r
7015     case IDM_Copy:\r
7016       SendMessage(hwnd, WM_COPY, 0, 0);\r
7017       return 0;\r
7018     default:\r
7019       {\r
7020         int i = LOWORD(wParam) - IDM_CommandX;\r
7021         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7022             icsTextMenuEntry[i].command != NULL) {\r
7023           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7024                    icsTextMenuEntry[i].getname,\r
7025                    icsTextMenuEntry[i].immediate);\r
7026           return 0;\r
7027         }\r
7028       }\r
7029       break;\r
7030     }\r
7031     break;\r
7032   }\r
7033   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7034 }\r
7035 \r
7036 WNDPROC consoleInputWindowProc;\r
7037 \r
7038 LRESULT CALLBACK\r
7039 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7040 {\r
7041   char buf[MSG_SIZ];\r
7042   char *p;\r
7043   static BOOL sendNextChar = FALSE;\r
7044   static BOOL quoteNextChar = FALSE;\r
7045   InputSource *is = consoleInputSource;\r
7046   CHARFORMAT cf;\r
7047   CHARRANGE sel;\r
7048 \r
7049   switch (message) {\r
7050   case WM_CHAR:\r
7051     if (!appData.localLineEditing || sendNextChar) {\r
7052       is->buf[0] = (CHAR) wParam;\r
7053       is->count = 1;\r
7054       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7055       sendNextChar = FALSE;\r
7056       return 0;\r
7057     }\r
7058     if (quoteNextChar) {\r
7059       buf[0] = (char) wParam;\r
7060       buf[1] = NULLCHAR;\r
7061       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7062       quoteNextChar = FALSE;\r
7063       return 0;\r
7064     }\r
7065     switch (wParam) {\r
7066     case '\r':   /* Enter key */\r
7067       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7068       if (consoleEcho) SaveInHistory(is->buf);\r
7069       is->buf[is->count++] = '\n';\r
7070       is->buf[is->count] = NULLCHAR;\r
7071       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7072       if (consoleEcho) {\r
7073         ConsoleOutput(is->buf, is->count, TRUE);\r
7074       } else if (appData.localLineEditing) {\r
7075         ConsoleOutput("\n", 1, TRUE);\r
7076       }\r
7077       /* fall thru */\r
7078     case '\033': /* Escape key */\r
7079       SetWindowText(hwnd, "");\r
7080       cf.cbSize = sizeof(CHARFORMAT);\r
7081       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7082       if (consoleEcho) {\r
7083         cf.crTextColor = textAttribs[ColorNormal].color;\r
7084       } else {\r
7085         cf.crTextColor = COLOR_ECHOOFF;\r
7086       }\r
7087       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7088       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7089       return 0;\r
7090     case '\t':   /* Tab key */\r
7091       if (GetKeyState(VK_SHIFT) < 0) {\r
7092         /* shifted */\r
7093         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7094       } else {\r
7095         /* unshifted */\r
7096         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7097         if (buttonDesc[0].hwnd) {\r
7098           SetFocus(buttonDesc[0].hwnd);\r
7099         } else {\r
7100           SetFocus(hwndMain);\r
7101         }\r
7102       }\r
7103       return 0;\r
7104     case '\023': /* Ctrl+S */\r
7105       sendNextChar = TRUE;\r
7106       return 0;\r
7107     case '\021': /* Ctrl+Q */\r
7108       quoteNextChar = TRUE;\r
7109       return 0;\r
7110     JAWS_REPLAY\r
7111     default:\r
7112       break;\r
7113     }\r
7114     break;\r
7115   case WM_KEYDOWN:\r
7116     switch (wParam) {\r
7117     case VK_UP:\r
7118       GetWindowText(hwnd, buf, MSG_SIZ);\r
7119       p = PrevInHistory(buf);\r
7120       if (p != NULL) {\r
7121         SetWindowText(hwnd, p);\r
7122         sel.cpMin = 999999;\r
7123         sel.cpMax = 999999;\r
7124         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7125         return 0;\r
7126       }\r
7127       break;\r
7128     case VK_DOWN:\r
7129       p = NextInHistory();\r
7130       if (p != NULL) {\r
7131         SetWindowText(hwnd, p);\r
7132         sel.cpMin = 999999;\r
7133         sel.cpMax = 999999;\r
7134         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7135         return 0;\r
7136       }\r
7137       break;\r
7138     case VK_HOME:\r
7139     case VK_END:\r
7140       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7141       /* fall thru */\r
7142     case VK_PRIOR:\r
7143     case VK_NEXT:\r
7144       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7145       return 0;\r
7146     }\r
7147     break;\r
7148   case WM_MBUTTONDOWN:\r
7149     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7150       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7151     break;\r
7152   case WM_RBUTTONUP:\r
7153     if (GetKeyState(VK_SHIFT) & ~1) {\r
7154       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7155         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7156     } else {\r
7157       POINT pt;\r
7158       HMENU hmenu;\r
7159       hmenu = LoadMenu(hInst, "InputMenu");\r
7160       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7161       if (sel.cpMin == sel.cpMax) {\r
7162         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7163         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7164       }\r
7165       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7166         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7167       }\r
7168       pt.x = LOWORD(lParam);\r
7169       pt.y = HIWORD(lParam);\r
7170       MenuPopup(hwnd, pt, hmenu, -1);\r
7171     }\r
7172     return 0;\r
7173   case WM_COMMAND:\r
7174     switch (LOWORD(wParam)) { \r
7175     case IDM_Undo:\r
7176       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7177       return 0;\r
7178     case IDM_SelectAll:\r
7179       sel.cpMin = 0;\r
7180       sel.cpMax = -1; /*999999?*/\r
7181       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7182       return 0;\r
7183     case IDM_Cut:\r
7184       SendMessage(hwnd, WM_CUT, 0, 0);\r
7185       return 0;\r
7186     case IDM_Paste:\r
7187       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7188       return 0;\r
7189     case IDM_Copy:\r
7190       SendMessage(hwnd, WM_COPY, 0, 0);\r
7191       return 0;\r
7192     }\r
7193     break;\r
7194   }\r
7195   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7196 }\r
7197 \r
7198 #define CO_MAX  100000\r
7199 #define CO_TRIM   1000\r
7200 \r
7201 LRESULT CALLBACK\r
7202 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7203 {\r
7204   static SnapData sd;\r
7205   HWND hText, hInput;\r
7206   RECT rect;\r
7207   static int sizeX, sizeY;\r
7208   int newSizeX, newSizeY;\r
7209   MINMAXINFO *mmi;\r
7210   WORD wMask;\r
7211 \r
7212   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7213   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7214 \r
7215   switch (message) {\r
7216   case WM_NOTIFY:\r
7217     if (((NMHDR*)lParam)->code == EN_LINK)\r
7218     {\r
7219       ENLINK *pLink = (ENLINK*)lParam;\r
7220       if (pLink->msg == WM_LBUTTONUP)\r
7221       {\r
7222         TEXTRANGE tr;\r
7223 \r
7224         tr.chrg = pLink->chrg;\r
7225         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7226         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7227         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7228         free(tr.lpstrText);\r
7229       }\r
7230     }\r
7231     break;\r
7232   case WM_INITDIALOG: /* message: initialize dialog box */\r
7233     hwndConsole = hDlg;\r
7234     SetFocus(hInput);\r
7235     consoleTextWindowProc = (WNDPROC)\r
7236       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7237     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7238     consoleInputWindowProc = (WNDPROC)\r
7239       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7240     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7241     Colorize(ColorNormal, TRUE);\r
7242     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7243     ChangedConsoleFont();\r
7244     GetClientRect(hDlg, &rect);\r
7245     sizeX = rect.right;\r
7246     sizeY = rect.bottom;\r
7247     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7248         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7249       WINDOWPLACEMENT wp;\r
7250       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7251       wp.length = sizeof(WINDOWPLACEMENT);\r
7252       wp.flags = 0;\r
7253       wp.showCmd = SW_SHOW;\r
7254       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7255       wp.rcNormalPosition.left = wpConsole.x;\r
7256       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7257       wp.rcNormalPosition.top = wpConsole.y;\r
7258       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7259       SetWindowPlacement(hDlg, &wp);\r
7260     }\r
7261 \r
7262    // [HGM] Chessknight's change 2004-07-13\r
7263    else { /* Determine Defaults */\r
7264        WINDOWPLACEMENT wp;\r
7265        wpConsole.x = wpMain.width + 1;\r
7266        wpConsole.y = wpMain.y;\r
7267        wpConsole.width = screenWidth -  wpMain.width;\r
7268        wpConsole.height = wpMain.height;\r
7269        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7270        wp.length = sizeof(WINDOWPLACEMENT);\r
7271        wp.flags = 0;\r
7272        wp.showCmd = SW_SHOW;\r
7273        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7274        wp.rcNormalPosition.left = wpConsole.x;\r
7275        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7276        wp.rcNormalPosition.top = wpConsole.y;\r
7277        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7278        SetWindowPlacement(hDlg, &wp);\r
7279     }\r
7280 \r
7281    // Allow hText to highlight URLs and send notifications on them\r
7282    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7283    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7284    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7285    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7286 \r
7287     return FALSE;\r
7288 \r
7289   case WM_SETFOCUS:\r
7290     SetFocus(hInput);\r
7291     return 0;\r
7292 \r
7293   case WM_CLOSE:\r
7294     ExitEvent(0);\r
7295     /* not reached */\r
7296     break;\r
7297 \r
7298   case WM_SIZE:\r
7299     if (IsIconic(hDlg)) break;\r
7300     newSizeX = LOWORD(lParam);\r
7301     newSizeY = HIWORD(lParam);\r
7302     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7303       RECT rectText, rectInput;\r
7304       POINT pt;\r
7305       int newTextHeight, newTextWidth;\r
7306       GetWindowRect(hText, &rectText);\r
7307       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7308       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7309       if (newTextHeight < 0) {\r
7310         newSizeY += -newTextHeight;\r
7311         newTextHeight = 0;\r
7312       }\r
7313       SetWindowPos(hText, NULL, 0, 0,\r
7314         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7315       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7316       pt.x = rectInput.left;\r
7317       pt.y = rectInput.top + newSizeY - sizeY;\r
7318       ScreenToClient(hDlg, &pt);\r
7319       SetWindowPos(hInput, NULL, \r
7320         pt.x, pt.y, /* needs client coords */   \r
7321         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7322         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7323     }\r
7324     sizeX = newSizeX;\r
7325     sizeY = newSizeY;\r
7326     break;\r
7327 \r
7328   case WM_GETMINMAXINFO:\r
7329     /* Prevent resizing window too small */\r
7330     mmi = (MINMAXINFO *) lParam;\r
7331     mmi->ptMinTrackSize.x = 100;\r
7332     mmi->ptMinTrackSize.y = 100;\r
7333     break;\r
7334 \r
7335   /* [AS] Snapping */\r
7336   case WM_ENTERSIZEMOVE:\r
7337     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7338 \r
7339   case WM_SIZING:\r
7340     return OnSizing( &sd, hDlg, wParam, lParam );\r
7341 \r
7342   case WM_MOVING:\r
7343     return OnMoving( &sd, hDlg, wParam, lParam );\r
7344 \r
7345   case WM_EXITSIZEMOVE:\r
7346         UpdateICSWidth(hText);\r
7347     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7348   }\r
7349 \r
7350   return DefWindowProc(hDlg, message, wParam, lParam);\r
7351 }\r
7352 \r
7353 \r
7354 VOID\r
7355 ConsoleCreate()\r
7356 {\r
7357   HWND hCons;\r
7358   if (hwndConsole) return;\r
7359   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7360   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7361 }\r
7362 \r
7363 \r
7364 VOID\r
7365 ConsoleOutput(char* data, int length, int forceVisible)\r
7366 {\r
7367   HWND hText;\r
7368   int trim, exlen;\r
7369   char *p, *q;\r
7370   char buf[CO_MAX+1];\r
7371   POINT pEnd;\r
7372   RECT rect;\r
7373   static int delayLF = 0;\r
7374   CHARRANGE savesel, sel;\r
7375 \r
7376   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7377   p = data;\r
7378   q = buf;\r
7379   if (delayLF) {\r
7380     *q++ = '\r';\r
7381     *q++ = '\n';\r
7382     delayLF = 0;\r
7383   }\r
7384   while (length--) {\r
7385     if (*p == '\n') {\r
7386       if (*++p) {\r
7387         *q++ = '\r';\r
7388         *q++ = '\n';\r
7389       } else {\r
7390         delayLF = 1;\r
7391       }\r
7392     } else if (*p == '\007') {\r
7393        MyPlaySound(&sounds[(int)SoundBell]);\r
7394        p++;\r
7395     } else {\r
7396       *q++ = *p++;\r
7397     }\r
7398   }\r
7399   *q = NULLCHAR;\r
7400   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7401   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7402   /* Save current selection */\r
7403   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7404   exlen = GetWindowTextLength(hText);\r
7405   /* Find out whether current end of text is visible */\r
7406   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7407   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7408   /* Trim existing text if it's too long */\r
7409   if (exlen + (q - buf) > CO_MAX) {\r
7410     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7411     sel.cpMin = 0;\r
7412     sel.cpMax = trim;\r
7413     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7414     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7415     exlen -= trim;\r
7416     savesel.cpMin -= trim;\r
7417     savesel.cpMax -= trim;\r
7418     if (exlen < 0) exlen = 0;\r
7419     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7420     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7421   }\r
7422   /* Append the new text */\r
7423   sel.cpMin = exlen;\r
7424   sel.cpMax = exlen;\r
7425   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7426   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7427   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7428   if (forceVisible || exlen == 0 ||\r
7429       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7430        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7431     /* Scroll to make new end of text visible if old end of text\r
7432        was visible or new text is an echo of user typein */\r
7433     sel.cpMin = 9999999;\r
7434     sel.cpMax = 9999999;\r
7435     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7436     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7437     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7438     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7439   }\r
7440   if (savesel.cpMax == exlen || forceVisible) {\r
7441     /* Move insert point to new end of text if it was at the old\r
7442        end of text or if the new text is an echo of user typein */\r
7443     sel.cpMin = 9999999;\r
7444     sel.cpMax = 9999999;\r
7445     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7446   } else {\r
7447     /* Restore previous selection */\r
7448     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7449   }\r
7450   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7451 }\r
7452 \r
7453 /*---------*/\r
7454 \r
7455 \r
7456 void\r
7457 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7458 {\r
7459   char buf[100];\r
7460   char *str;\r
7461   COLORREF oldFg, oldBg;\r
7462   HFONT oldFont;\r
7463   RECT rect;\r
7464 \r
7465   if(copyNumber > 1)\r
7466     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7467 \r
7468   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7469   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7470   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7471 \r
7472   rect.left = x;\r
7473   rect.right = x + squareSize;\r
7474   rect.top  = y;\r
7475   rect.bottom = y + squareSize;\r
7476   str = buf;\r
7477 \r
7478   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7479                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7480              y, ETO_CLIPPED|ETO_OPAQUE,\r
7481              &rect, str, strlen(str), NULL);\r
7482 \r
7483   (void) SetTextColor(hdc, oldFg);\r
7484   (void) SetBkColor(hdc, oldBg);\r
7485   (void) SelectObject(hdc, oldFont);\r
7486 }\r
7487 \r
7488 void\r
7489 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7490               RECT *rect, char *color, char *flagFell)\r
7491 {\r
7492   char buf[100];\r
7493   char *str;\r
7494   COLORREF oldFg, oldBg;\r
7495   HFONT oldFont;\r
7496 \r
7497   if (appData.clockMode) {\r
7498     if (tinyLayout)\r
7499       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7500     else\r
7501       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7502     str = buf;\r
7503   } else {\r
7504     str = color;\r
7505   }\r
7506 \r
7507   if (highlight) {\r
7508     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7509     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7510   } else {\r
7511     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7512     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7513   }\r
7514   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7515 \r
7516   JAWS_SILENCE\r
7517 \r
7518   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7519              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7520              rect, str, strlen(str), NULL);\r
7521   if(logoHeight > 0 && appData.clockMode) {\r
7522       RECT r;\r
7523       str += strlen(color)+2;\r
7524       r.top = rect->top + logoHeight/2;\r
7525       r.left = rect->left;\r
7526       r.right = rect->right;\r
7527       r.bottom = rect->bottom;\r
7528       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7529                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7530                  &r, str, strlen(str), NULL);\r
7531   }\r
7532   (void) SetTextColor(hdc, oldFg);\r
7533   (void) SetBkColor(hdc, oldBg);\r
7534   (void) SelectObject(hdc, oldFont);\r
7535 }\r
7536 \r
7537 \r
7538 int\r
7539 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7540            OVERLAPPED *ovl)\r
7541 {\r
7542   int ok, err;\r
7543 \r
7544   /* [AS]  */\r
7545   if( count <= 0 ) {\r
7546     if (appData.debugMode) {\r
7547       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7548     }\r
7549 \r
7550     return ERROR_INVALID_USER_BUFFER;\r
7551   }\r
7552 \r
7553   ResetEvent(ovl->hEvent);\r
7554   ovl->Offset = ovl->OffsetHigh = 0;\r
7555   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7556   if (ok) {\r
7557     err = NO_ERROR;\r
7558   } else {\r
7559     err = GetLastError();\r
7560     if (err == ERROR_IO_PENDING) {\r
7561       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7562       if (ok)\r
7563         err = NO_ERROR;\r
7564       else\r
7565         err = GetLastError();\r
7566     }\r
7567   }\r
7568   return err;\r
7569 }\r
7570 \r
7571 int\r
7572 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7573             OVERLAPPED *ovl)\r
7574 {\r
7575   int ok, err;\r
7576 \r
7577   ResetEvent(ovl->hEvent);\r
7578   ovl->Offset = ovl->OffsetHigh = 0;\r
7579   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7580   if (ok) {\r
7581     err = NO_ERROR;\r
7582   } else {\r
7583     err = GetLastError();\r
7584     if (err == ERROR_IO_PENDING) {\r
7585       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7586       if (ok)\r
7587         err = NO_ERROR;\r
7588       else\r
7589         err = GetLastError();\r
7590     }\r
7591   }\r
7592   return err;\r
7593 }\r
7594 \r
7595 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7596 void CheckForInputBufferFull( InputSource * is )\r
7597 {\r
7598     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7599         /* Look for end of line */\r
7600         char * p = is->buf;\r
7601         \r
7602         while( p < is->next && *p != '\n' ) {\r
7603             p++;\r
7604         }\r
7605 \r
7606         if( p >= is->next ) {\r
7607             if (appData.debugMode) {\r
7608                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7609             }\r
7610 \r
7611             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7612             is->count = (DWORD) -1;\r
7613             is->next = is->buf;\r
7614         }\r
7615     }\r
7616 }\r
7617 \r
7618 DWORD\r
7619 InputThread(LPVOID arg)\r
7620 {\r
7621   InputSource *is;\r
7622   OVERLAPPED ovl;\r
7623 \r
7624   is = (InputSource *) arg;\r
7625   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7626   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7627   while (is->hThread != NULL) {\r
7628     is->error = DoReadFile(is->hFile, is->next,\r
7629                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7630                            &is->count, &ovl);\r
7631     if (is->error == NO_ERROR) {\r
7632       is->next += is->count;\r
7633     } else {\r
7634       if (is->error == ERROR_BROKEN_PIPE) {\r
7635         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7636         is->count = 0;\r
7637       } else {\r
7638         is->count = (DWORD) -1;\r
7639         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7640         break; \r
7641       }\r
7642     }\r
7643 \r
7644     CheckForInputBufferFull( is );\r
7645 \r
7646     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7647 \r
7648     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7649 \r
7650     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7651   }\r
7652 \r
7653   CloseHandle(ovl.hEvent);\r
7654   CloseHandle(is->hFile);\r
7655 \r
7656   if (appData.debugMode) {\r
7657     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7658   }\r
7659 \r
7660   return 0;\r
7661 }\r
7662 \r
7663 \r
7664 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7665 DWORD\r
7666 NonOvlInputThread(LPVOID arg)\r
7667 {\r
7668   InputSource *is;\r
7669   char *p, *q;\r
7670   int i;\r
7671   char prev;\r
7672 \r
7673   is = (InputSource *) arg;\r
7674   while (is->hThread != NULL) {\r
7675     is->error = ReadFile(is->hFile, is->next,\r
7676                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7677                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7678     if (is->error == NO_ERROR) {\r
7679       /* Change CRLF to LF */\r
7680       if (is->next > is->buf) {\r
7681         p = is->next - 1;\r
7682         i = is->count + 1;\r
7683       } else {\r
7684         p = is->next;\r
7685         i = is->count;\r
7686       }\r
7687       q = p;\r
7688       prev = NULLCHAR;\r
7689       while (i > 0) {\r
7690         if (prev == '\r' && *p == '\n') {\r
7691           *(q-1) = '\n';\r
7692           is->count--;\r
7693         } else { \r
7694           *q++ = *p;\r
7695         }\r
7696         prev = *p++;\r
7697         i--;\r
7698       }\r
7699       *q = NULLCHAR;\r
7700       is->next = q;\r
7701     } else {\r
7702       if (is->error == ERROR_BROKEN_PIPE) {\r
7703         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7704         is->count = 0; \r
7705       } else {\r
7706         is->count = (DWORD) -1;\r
7707       }\r
7708     }\r
7709 \r
7710     CheckForInputBufferFull( is );\r
7711 \r
7712     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7713 \r
7714     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7715 \r
7716     if (is->count < 0) break;  /* Quit on error */\r
7717   }\r
7718   CloseHandle(is->hFile);\r
7719   return 0;\r
7720 }\r
7721 \r
7722 DWORD\r
7723 SocketInputThread(LPVOID arg)\r
7724 {\r
7725   InputSource *is;\r
7726 \r
7727   is = (InputSource *) arg;\r
7728   while (is->hThread != NULL) {\r
7729     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7730     if ((int)is->count == SOCKET_ERROR) {\r
7731       is->count = (DWORD) -1;\r
7732       is->error = WSAGetLastError();\r
7733     } else {\r
7734       is->error = NO_ERROR;\r
7735       is->next += is->count;\r
7736       if (is->count == 0 && is->second == is) {\r
7737         /* End of file on stderr; quit with no message */\r
7738         break;\r
7739       }\r
7740     }\r
7741     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7742 \r
7743     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7744 \r
7745     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7746   }\r
7747   return 0;\r
7748 }\r
7749 \r
7750 VOID\r
7751 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7752 {\r
7753   InputSource *is;\r
7754 \r
7755   is = (InputSource *) lParam;\r
7756   if (is->lineByLine) {\r
7757     /* Feed in lines one by one */\r
7758     char *p = is->buf;\r
7759     char *q = p;\r
7760     while (q < is->next) {\r
7761       if (*q++ == '\n') {\r
7762         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7763         p = q;\r
7764       }\r
7765     }\r
7766     \r
7767     /* Move any partial line to the start of the buffer */\r
7768     q = is->buf;\r
7769     while (p < is->next) {\r
7770       *q++ = *p++;\r
7771     }\r
7772     is->next = q;\r
7773 \r
7774     if (is->error != NO_ERROR || is->count == 0) {\r
7775       /* Notify backend of the error.  Note: If there was a partial\r
7776          line at the end, it is not flushed through. */\r
7777       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7778     }\r
7779   } else {\r
7780     /* Feed in the whole chunk of input at once */\r
7781     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7782     is->next = is->buf;\r
7783   }\r
7784 }\r
7785 \r
7786 /*---------------------------------------------------------------------------*\\r
7787  *\r
7788  *  Menu enables. Used when setting various modes.\r
7789  *\r
7790 \*---------------------------------------------------------------------------*/\r
7791 \r
7792 typedef struct {\r
7793   int item;\r
7794   int flags;\r
7795 } Enables;\r
7796 \r
7797 VOID\r
7798 GreyRevert(Boolean grey)\r
7799 { // [HGM] vari: for retracting variations in local mode\r
7800   HMENU hmenu = GetMenu(hwndMain);\r
7801   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7802   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7803 }\r
7804 \r
7805 VOID\r
7806 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7807 {\r
7808   while (enab->item > 0) {\r
7809     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7810     enab++;\r
7811   }\r
7812 }\r
7813 \r
7814 Enables gnuEnables[] = {\r
7815   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7828 \r
7829   // Needed to switch from ncp to GNU mode on Engine Load\r
7830   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7831   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7833   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7835   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7836   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7837   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7838   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7841   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7844   { -1, -1 }\r
7845 };\r
7846 \r
7847 Enables icsEnables[] = {\r
7848   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7867   { -1, -1 }\r
7868 };\r
7869 \r
7870 #if ZIPPY\r
7871 Enables zippyEnables[] = {\r
7872   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7876   { -1, -1 }\r
7877 };\r
7878 #endif\r
7879 \r
7880 Enables ncpEnables[] = {\r
7881   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7890   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7891   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7903   { -1, -1 }\r
7904 };\r
7905 \r
7906 Enables trainingOnEnables[] = {\r
7907   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7916   { -1, -1 }\r
7917 };\r
7918 \r
7919 Enables trainingOffEnables[] = {\r
7920   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7929   { -1, -1 }\r
7930 };\r
7931 \r
7932 /* These modify either ncpEnables or gnuEnables */\r
7933 Enables cmailEnables[] = {\r
7934   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7936   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7937   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7941   { -1, -1 }\r
7942 };\r
7943 \r
7944 Enables machineThinkingEnables[] = {\r
7945   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7958 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7961   { -1, -1 }\r
7962 };\r
7963 \r
7964 Enables userThinkingEnables[] = {\r
7965   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7967   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7968   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7969   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7971   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7978 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7981   { -1, -1 }\r
7982 };\r
7983 \r
7984 /*---------------------------------------------------------------------------*\\r
7985  *\r
7986  *  Front-end interface functions exported by XBoard.\r
7987  *  Functions appear in same order as prototypes in frontend.h.\r
7988  * \r
7989 \*---------------------------------------------------------------------------*/\r
7990 VOID\r
7991 CheckMark(UINT item, int state)\r
7992 {\r
7993     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7994 }\r
7995 \r
7996 VOID\r
7997 ModeHighlight()\r
7998 {\r
7999   static UINT prevChecked = 0;\r
8000   static int prevPausing = 0;\r
8001   UINT nowChecked;\r
8002 \r
8003   if (pausing != prevPausing) {\r
8004     prevPausing = pausing;\r
8005     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8006                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8007     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8008   }\r
8009 \r
8010   switch (gameMode) {\r
8011   case BeginningOfGame:\r
8012     if (appData.icsActive)\r
8013       nowChecked = IDM_IcsClient;\r
8014     else if (appData.noChessProgram)\r
8015       nowChecked = IDM_EditGame;\r
8016     else\r
8017       nowChecked = IDM_MachineBlack;\r
8018     break;\r
8019   case MachinePlaysBlack:\r
8020     nowChecked = IDM_MachineBlack;\r
8021     break;\r
8022   case MachinePlaysWhite:\r
8023     nowChecked = IDM_MachineWhite;\r
8024     break;\r
8025   case TwoMachinesPlay:\r
8026     nowChecked = IDM_TwoMachines;\r
8027     break;\r
8028   case AnalyzeMode:\r
8029     nowChecked = IDM_AnalysisMode;\r
8030     break;\r
8031   case AnalyzeFile:\r
8032     nowChecked = IDM_AnalyzeFile;\r
8033     break;\r
8034   case EditGame:\r
8035     nowChecked = IDM_EditGame;\r
8036     break;\r
8037   case PlayFromGameFile:\r
8038     nowChecked = IDM_LoadGame;\r
8039     break;\r
8040   case EditPosition:\r
8041     nowChecked = IDM_EditPosition;\r
8042     break;\r
8043   case Training:\r
8044     nowChecked = IDM_Training;\r
8045     break;\r
8046   case IcsPlayingWhite:\r
8047   case IcsPlayingBlack:\r
8048   case IcsObserving:\r
8049   case IcsIdle:\r
8050     nowChecked = IDM_IcsClient;\r
8051     break;\r
8052   default:\r
8053   case EndOfGame:\r
8054     nowChecked = 0;\r
8055     break;\r
8056   }\r
8057   CheckMark(prevChecked, MF_UNCHECKED);\r
8058   CheckMark(nowChecked, MF_CHECKED);\r
8059   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8060 \r
8061   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8062     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8063                           MF_BYCOMMAND|MF_ENABLED);\r
8064   } else {\r
8065     (void) EnableMenuItem(GetMenu(hwndMain), \r
8066                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8067   }\r
8068 \r
8069   prevChecked = nowChecked;\r
8070 \r
8071   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8072   if (appData.icsActive) {\r
8073        if (appData.icsEngineAnalyze) {\r
8074                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8075        } else {\r
8076                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8077        }\r
8078   }\r
8079   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8080 }\r
8081 \r
8082 VOID\r
8083 SetICSMode()\r
8084 {\r
8085   HMENU hmenu = GetMenu(hwndMain);\r
8086   SetMenuEnables(hmenu, icsEnables);\r
8087   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8088     MF_BYCOMMAND|MF_ENABLED);\r
8089 #if ZIPPY\r
8090   if (appData.zippyPlay) {\r
8091     SetMenuEnables(hmenu, zippyEnables);\r
8092     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8093          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8094           MF_BYCOMMAND|MF_ENABLED);\r
8095   }\r
8096 #endif\r
8097 }\r
8098 \r
8099 VOID\r
8100 SetGNUMode()\r
8101 {\r
8102   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8103 }\r
8104 \r
8105 VOID\r
8106 SetNCPMode()\r
8107 {\r
8108   HMENU hmenu = GetMenu(hwndMain);\r
8109   SetMenuEnables(hmenu, ncpEnables);\r
8110     DrawMenuBar(hwndMain);\r
8111 }\r
8112 \r
8113 VOID\r
8114 SetCmailMode()\r
8115 {\r
8116   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8117 }\r
8118 \r
8119 VOID \r
8120 SetTrainingModeOn()\r
8121 {\r
8122   int i;\r
8123   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8124   for (i = 0; i < N_BUTTONS; i++) {\r
8125     if (buttonDesc[i].hwnd != NULL)\r
8126       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8127   }\r
8128   CommentPopDown();\r
8129 }\r
8130 \r
8131 VOID SetTrainingModeOff()\r
8132 {\r
8133   int i;\r
8134   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8135   for (i = 0; i < N_BUTTONS; i++) {\r
8136     if (buttonDesc[i].hwnd != NULL)\r
8137       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8138   }\r
8139 }\r
8140 \r
8141 \r
8142 VOID\r
8143 SetUserThinkingEnables()\r
8144 {\r
8145   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8146 }\r
8147 \r
8148 VOID\r
8149 SetMachineThinkingEnables()\r
8150 {\r
8151   HMENU hMenu = GetMenu(hwndMain);\r
8152   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8153 \r
8154   SetMenuEnables(hMenu, machineThinkingEnables);\r
8155 \r
8156   if (gameMode == MachinePlaysBlack) {\r
8157     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8158   } else if (gameMode == MachinePlaysWhite) {\r
8159     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8160   } else if (gameMode == TwoMachinesPlay) {\r
8161     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8162   }\r
8163 }\r
8164 \r
8165 \r
8166 VOID\r
8167 DisplayTitle(char *str)\r
8168 {\r
8169   char title[MSG_SIZ], *host;\r
8170   if (str[0] != NULLCHAR) {\r
8171     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8172   } else if (appData.icsActive) {\r
8173     if (appData.icsCommPort[0] != NULLCHAR)\r
8174       host = "ICS";\r
8175     else \r
8176       host = appData.icsHost;\r
8177       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8178   } else if (appData.noChessProgram) {\r
8179     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8180   } else {\r
8181     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8182     strcat(title, ": ");\r
8183     strcat(title, first.tidy);\r
8184   }\r
8185   SetWindowText(hwndMain, title);\r
8186 }\r
8187 \r
8188 \r
8189 VOID\r
8190 DisplayMessage(char *str1, char *str2)\r
8191 {\r
8192   HDC hdc;\r
8193   HFONT oldFont;\r
8194   int remain = MESSAGE_TEXT_MAX - 1;\r
8195   int len;\r
8196 \r
8197   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8198   messageText[0] = NULLCHAR;\r
8199   if (*str1) {\r
8200     len = strlen(str1);\r
8201     if (len > remain) len = remain;\r
8202     strncpy(messageText, str1, len);\r
8203     messageText[len] = NULLCHAR;\r
8204     remain -= len;\r
8205   }\r
8206   if (*str2 && remain >= 2) {\r
8207     if (*str1) {\r
8208       strcat(messageText, "  ");\r
8209       remain -= 2;\r
8210     }\r
8211     len = strlen(str2);\r
8212     if (len > remain) len = remain;\r
8213     strncat(messageText, str2, len);\r
8214   }\r
8215   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8216   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8217 \r
8218   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8219 \r
8220   SAYMACHINEMOVE();\r
8221 \r
8222   hdc = GetDC(hwndMain);\r
8223   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8224   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8225              &messageRect, messageText, strlen(messageText), NULL);\r
8226   (void) SelectObject(hdc, oldFont);\r
8227   (void) ReleaseDC(hwndMain, hdc);\r
8228 }\r
8229 \r
8230 VOID\r
8231 DisplayError(char *str, int error)\r
8232 {\r
8233   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8234   int len;\r
8235 \r
8236   if (error == 0) {\r
8237     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8238   } else {\r
8239     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8240                         NULL, error, LANG_NEUTRAL,\r
8241                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8242     if (len > 0) {\r
8243       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8244     } else {\r
8245       ErrorMap *em = errmap;\r
8246       while (em->err != 0 && em->err != error) em++;\r
8247       if (em->err != 0) {\r
8248         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8249       } else {\r
8250         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8251       }\r
8252     }\r
8253   }\r
8254   \r
8255   ErrorPopUp(_("Error"), buf);\r
8256 }\r
8257 \r
8258 \r
8259 VOID\r
8260 DisplayMoveError(char *str)\r
8261 {\r
8262   fromX = fromY = -1;\r
8263   ClearHighlights();\r
8264   DrawPosition(FALSE, NULL);\r
8265   if (appData.popupMoveErrors) {\r
8266     ErrorPopUp(_("Error"), str);\r
8267   } else {\r
8268     DisplayMessage(str, "");\r
8269     moveErrorMessageUp = TRUE;\r
8270   }\r
8271 }\r
8272 \r
8273 VOID\r
8274 DisplayFatalError(char *str, int error, int exitStatus)\r
8275 {\r
8276   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8277   int len;\r
8278   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8279 \r
8280   if (error != 0) {\r
8281     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8282                         NULL, error, LANG_NEUTRAL,\r
8283                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8284     if (len > 0) {\r
8285       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8286     } else {\r
8287       ErrorMap *em = errmap;\r
8288       while (em->err != 0 && em->err != error) em++;\r
8289       if (em->err != 0) {\r
8290         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8291       } else {\r
8292         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8293       }\r
8294     }\r
8295     str = buf;\r
8296   }\r
8297   if (appData.debugMode) {\r
8298     fprintf(debugFP, "%s: %s\n", label, str);\r
8299   }\r
8300   if (appData.popupExitMessage) {\r
8301     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8302                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8303   }\r
8304   ExitEvent(exitStatus);\r
8305 }\r
8306 \r
8307 \r
8308 VOID\r
8309 DisplayInformation(char *str)\r
8310 {\r
8311   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8312 }\r
8313 \r
8314 \r
8315 VOID\r
8316 DisplayNote(char *str)\r
8317 {\r
8318   ErrorPopUp(_("Note"), str);\r
8319 }\r
8320 \r
8321 \r
8322 typedef struct {\r
8323   char *title, *question, *replyPrefix;\r
8324   ProcRef pr;\r
8325 } QuestionParams;\r
8326 \r
8327 LRESULT CALLBACK\r
8328 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8329 {\r
8330   static QuestionParams *qp;\r
8331   char reply[MSG_SIZ];\r
8332   int len, err;\r
8333 \r
8334   switch (message) {\r
8335   case WM_INITDIALOG:\r
8336     qp = (QuestionParams *) lParam;\r
8337     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8338     Translate(hDlg, DLG_Question);\r
8339     SetWindowText(hDlg, qp->title);\r
8340     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8341     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8342     return FALSE;\r
8343 \r
8344   case WM_COMMAND:\r
8345     switch (LOWORD(wParam)) {\r
8346     case IDOK:\r
8347       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8348       if (*reply) strcat(reply, " ");\r
8349       len = strlen(reply);\r
8350       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8351       strcat(reply, "\n");\r
8352       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8353       EndDialog(hDlg, TRUE);\r
8354       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8355       return TRUE;\r
8356     case IDCANCEL:\r
8357       EndDialog(hDlg, FALSE);\r
8358       return TRUE;\r
8359     default:\r
8360       break;\r
8361     }\r
8362     break;\r
8363   }\r
8364   return FALSE;\r
8365 }\r
8366 \r
8367 VOID\r
8368 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8369 {\r
8370     QuestionParams qp;\r
8371     FARPROC lpProc;\r
8372     \r
8373     qp.title = title;\r
8374     qp.question = question;\r
8375     qp.replyPrefix = replyPrefix;\r
8376     qp.pr = pr;\r
8377     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8378     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8379       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8380     FreeProcInstance(lpProc);\r
8381 }\r
8382 \r
8383 /* [AS] Pick FRC position */\r
8384 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8385 {\r
8386     static int * lpIndexFRC;\r
8387     BOOL index_is_ok;\r
8388     char buf[16];\r
8389 \r
8390     switch( message )\r
8391     {\r
8392     case WM_INITDIALOG:\r
8393         lpIndexFRC = (int *) lParam;\r
8394 \r
8395         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8396         Translate(hDlg, DLG_NewGameFRC);\r
8397 \r
8398         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8399         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8400         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8401         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8402 \r
8403         break;\r
8404 \r
8405     case WM_COMMAND:\r
8406         switch( LOWORD(wParam) ) {\r
8407         case IDOK:\r
8408             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8409             EndDialog( hDlg, 0 );\r
8410             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8411             return TRUE;\r
8412         case IDCANCEL:\r
8413             EndDialog( hDlg, 1 );   \r
8414             return TRUE;\r
8415         case IDC_NFG_Edit:\r
8416             if( HIWORD(wParam) == EN_CHANGE ) {\r
8417                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8418 \r
8419                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8420             }\r
8421             return TRUE;\r
8422         case IDC_NFG_Random:\r
8423           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8424             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8425             return TRUE;\r
8426         }\r
8427 \r
8428         break;\r
8429     }\r
8430 \r
8431     return FALSE;\r
8432 }\r
8433 \r
8434 int NewGameFRC()\r
8435 {\r
8436     int result;\r
8437     int index = appData.defaultFrcPosition;\r
8438     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8439 \r
8440     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8441 \r
8442     if( result == 0 ) {\r
8443         appData.defaultFrcPosition = index;\r
8444     }\r
8445 \r
8446     return result;\r
8447 }\r
8448 \r
8449 /* [AS] Game list options. Refactored by HGM */\r
8450 \r
8451 HWND gameListOptionsDialog;\r
8452 \r
8453 // low-level front-end: clear text edit / list widget\r
8454 void\r
8455 GLT_ClearList()\r
8456 {\r
8457     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8458 }\r
8459 \r
8460 // low-level front-end: clear text edit / list widget\r
8461 void\r
8462 GLT_DeSelectList()\r
8463 {\r
8464     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8465 }\r
8466 \r
8467 // low-level front-end: append line to text edit / list widget\r
8468 void\r
8469 GLT_AddToList( char *name )\r
8470 {\r
8471     if( name != 0 ) {\r
8472             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8473     }\r
8474 }\r
8475 \r
8476 // low-level front-end: get line from text edit / list widget\r
8477 Boolean\r
8478 GLT_GetFromList( int index, char *name )\r
8479 {\r
8480     if( name != 0 ) {\r
8481             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8482                 return TRUE;\r
8483     }\r
8484     return FALSE;\r
8485 }\r
8486 \r
8487 void GLT_MoveSelection( HWND hDlg, int delta )\r
8488 {\r
8489     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8490     int idx2 = idx1 + delta;\r
8491     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8492 \r
8493     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8494         char buf[128];\r
8495 \r
8496         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8497         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8498         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8499         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8500     }\r
8501 }\r
8502 \r
8503 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8504 {\r
8505     switch( message )\r
8506     {\r
8507     case WM_INITDIALOG:\r
8508         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8509         \r
8510         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8511         Translate(hDlg, DLG_GameListOptions);\r
8512 \r
8513         /* Initialize list */\r
8514         GLT_TagsToList( lpUserGLT );\r
8515 \r
8516         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8517 \r
8518         break;\r
8519 \r
8520     case WM_COMMAND:\r
8521         switch( LOWORD(wParam) ) {\r
8522         case IDOK:\r
8523             GLT_ParseList();\r
8524             EndDialog( hDlg, 0 );\r
8525             return TRUE;\r
8526         case IDCANCEL:\r
8527             EndDialog( hDlg, 1 );\r
8528             return TRUE;\r
8529 \r
8530         case IDC_GLT_Default:\r
8531             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8532             return TRUE;\r
8533 \r
8534         case IDC_GLT_Restore:\r
8535             GLT_TagsToList( appData.gameListTags );\r
8536             return TRUE;\r
8537 \r
8538         case IDC_GLT_Up:\r
8539             GLT_MoveSelection( hDlg, -1 );\r
8540             return TRUE;\r
8541 \r
8542         case IDC_GLT_Down:\r
8543             GLT_MoveSelection( hDlg, +1 );\r
8544             return TRUE;\r
8545         }\r
8546 \r
8547         break;\r
8548     }\r
8549 \r
8550     return FALSE;\r
8551 }\r
8552 \r
8553 int GameListOptions()\r
8554 {\r
8555     int result;\r
8556     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8557 \r
8558       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8559 \r
8560     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8561 \r
8562     if( result == 0 ) {\r
8563         /* [AS] Memory leak here! */\r
8564         appData.gameListTags = strdup( lpUserGLT ); \r
8565     }\r
8566 \r
8567     return result;\r
8568 }\r
8569 \r
8570 VOID\r
8571 DisplayIcsInteractionTitle(char *str)\r
8572 {\r
8573   char consoleTitle[MSG_SIZ];\r
8574 \r
8575     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8576     SetWindowText(hwndConsole, consoleTitle);\r
8577 \r
8578     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8579       char buf[MSG_SIZ], *p = buf, *q;\r
8580         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8581       do {\r
8582         q = strchr(p, ';');\r
8583         if(q) *q++ = 0;\r
8584         if(*p) ChatPopUp(p);\r
8585       } while(p=q);\r
8586     }\r
8587 \r
8588     SetActiveWindow(hwndMain);\r
8589 }\r
8590 \r
8591 void\r
8592 DrawPosition(int fullRedraw, Board board)\r
8593 {\r
8594   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8595 }\r
8596 \r
8597 void NotifyFrontendLogin()\r
8598 {\r
8599         if (hwndConsole)\r
8600                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8601 }\r
8602 \r
8603 VOID\r
8604 ResetFrontEnd()\r
8605 {\r
8606   fromX = fromY = -1;\r
8607   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8608     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8609     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8610     dragInfo.lastpos = dragInfo.pos;\r
8611     dragInfo.start.x = dragInfo.start.y = -1;\r
8612     dragInfo.from = dragInfo.start;\r
8613     ReleaseCapture();\r
8614     DrawPosition(TRUE, NULL);\r
8615   }\r
8616   TagsPopDown();\r
8617 }\r
8618 \r
8619 \r
8620 VOID\r
8621 CommentPopUp(char *title, char *str)\r
8622 {\r
8623   HWND hwnd = GetActiveWindow();\r
8624   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8625   SAY(str);\r
8626   SetActiveWindow(hwnd);\r
8627 }\r
8628 \r
8629 VOID\r
8630 CommentPopDown(void)\r
8631 {\r
8632   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8633   if (commentDialog) {\r
8634     ShowWindow(commentDialog, SW_HIDE);\r
8635   }\r
8636   commentUp = FALSE;\r
8637 }\r
8638 \r
8639 VOID\r
8640 EditCommentPopUp(int index, char *title, char *str)\r
8641 {\r
8642   EitherCommentPopUp(index, title, str, TRUE);\r
8643 }\r
8644 \r
8645 \r
8646 VOID\r
8647 RingBell()\r
8648 {\r
8649   MyPlaySound(&sounds[(int)SoundMove]);\r
8650 }\r
8651 \r
8652 VOID PlayIcsWinSound()\r
8653 {\r
8654   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8655 }\r
8656 \r
8657 VOID PlayIcsLossSound()\r
8658 {\r
8659   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8660 }\r
8661 \r
8662 VOID PlayIcsDrawSound()\r
8663 {\r
8664   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8665 }\r
8666 \r
8667 VOID PlayIcsUnfinishedSound()\r
8668 {\r
8669   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8670 }\r
8671 \r
8672 VOID\r
8673 PlayAlarmSound()\r
8674 {\r
8675   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8676 }\r
8677 \r
8678 VOID\r
8679 PlayTellSound()\r
8680 {\r
8681   MyPlaySound(&textAttribs[ColorTell].sound);\r
8682 }\r
8683 \r
8684 \r
8685 VOID\r
8686 EchoOn()\r
8687 {\r
8688   HWND hInput;\r
8689   consoleEcho = TRUE;\r
8690   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8691   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8692   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8693 }\r
8694 \r
8695 \r
8696 VOID\r
8697 EchoOff()\r
8698 {\r
8699   CHARFORMAT cf;\r
8700   HWND hInput;\r
8701   consoleEcho = FALSE;\r
8702   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8703   /* This works OK: set text and background both to the same color */\r
8704   cf = consoleCF;\r
8705   cf.crTextColor = COLOR_ECHOOFF;\r
8706   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8707   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8708 }\r
8709 \r
8710 /* No Raw()...? */\r
8711 \r
8712 void Colorize(ColorClass cc, int continuation)\r
8713 {\r
8714   currentColorClass = cc;\r
8715   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8716   consoleCF.crTextColor = textAttribs[cc].color;\r
8717   consoleCF.dwEffects = textAttribs[cc].effects;\r
8718   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8719 }\r
8720 \r
8721 char *\r
8722 UserName()\r
8723 {\r
8724   static char buf[MSG_SIZ];\r
8725   DWORD bufsiz = MSG_SIZ;\r
8726 \r
8727   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8728         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8729   }\r
8730   if (!GetUserName(buf, &bufsiz)) {\r
8731     /*DisplayError("Error getting user name", GetLastError());*/\r
8732     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8733   }\r
8734   return buf;\r
8735 }\r
8736 \r
8737 char *\r
8738 HostName()\r
8739 {\r
8740   static char buf[MSG_SIZ];\r
8741   DWORD bufsiz = MSG_SIZ;\r
8742 \r
8743   if (!GetComputerName(buf, &bufsiz)) {\r
8744     /*DisplayError("Error getting host name", GetLastError());*/\r
8745     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8746   }\r
8747   return buf;\r
8748 }\r
8749 \r
8750 \r
8751 int\r
8752 ClockTimerRunning()\r
8753 {\r
8754   return clockTimerEvent != 0;\r
8755 }\r
8756 \r
8757 int\r
8758 StopClockTimer()\r
8759 {\r
8760   if (clockTimerEvent == 0) return FALSE;\r
8761   KillTimer(hwndMain, clockTimerEvent);\r
8762   clockTimerEvent = 0;\r
8763   return TRUE;\r
8764 }\r
8765 \r
8766 void\r
8767 StartClockTimer(long millisec)\r
8768 {\r
8769   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8770                              (UINT) millisec, NULL);\r
8771 }\r
8772 \r
8773 void\r
8774 DisplayWhiteClock(long timeRemaining, int highlight)\r
8775 {\r
8776   HDC hdc;\r
8777   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8778 \r
8779   if(appData.noGUI) return;\r
8780   hdc = GetDC(hwndMain);\r
8781   if (!IsIconic(hwndMain)) {\r
8782     DisplayAClock(hdc, timeRemaining, highlight, \r
8783                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8784   }\r
8785   if (highlight && iconCurrent == iconBlack) {\r
8786     iconCurrent = iconWhite;\r
8787     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8788     if (IsIconic(hwndMain)) {\r
8789       DrawIcon(hdc, 2, 2, iconCurrent);\r
8790     }\r
8791   }\r
8792   (void) ReleaseDC(hwndMain, hdc);\r
8793   if (hwndConsole)\r
8794     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8795 }\r
8796 \r
8797 void\r
8798 DisplayBlackClock(long timeRemaining, int highlight)\r
8799 {\r
8800   HDC hdc;\r
8801   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8802 \r
8803   if(appData.noGUI) return;\r
8804   hdc = GetDC(hwndMain);\r
8805   if (!IsIconic(hwndMain)) {\r
8806     DisplayAClock(hdc, timeRemaining, highlight, \r
8807                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8808   }\r
8809   if (highlight && iconCurrent == iconWhite) {\r
8810     iconCurrent = iconBlack;\r
8811     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8812     if (IsIconic(hwndMain)) {\r
8813       DrawIcon(hdc, 2, 2, iconCurrent);\r
8814     }\r
8815   }\r
8816   (void) ReleaseDC(hwndMain, hdc);\r
8817   if (hwndConsole)\r
8818     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8819 }\r
8820 \r
8821 \r
8822 int\r
8823 LoadGameTimerRunning()\r
8824 {\r
8825   return loadGameTimerEvent != 0;\r
8826 }\r
8827 \r
8828 int\r
8829 StopLoadGameTimer()\r
8830 {\r
8831   if (loadGameTimerEvent == 0) return FALSE;\r
8832   KillTimer(hwndMain, loadGameTimerEvent);\r
8833   loadGameTimerEvent = 0;\r
8834   return TRUE;\r
8835 }\r
8836 \r
8837 void\r
8838 StartLoadGameTimer(long millisec)\r
8839 {\r
8840   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8841                                 (UINT) millisec, NULL);\r
8842 }\r
8843 \r
8844 void\r
8845 AutoSaveGame()\r
8846 {\r
8847   char *defName;\r
8848   FILE *f;\r
8849   char fileTitle[MSG_SIZ];\r
8850 \r
8851   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8852   f = OpenFileDialog(hwndMain, "a", defName,\r
8853                      appData.oldSaveStyle ? "gam" : "pgn",\r
8854                      GAME_FILT, \r
8855                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8856   if (f != NULL) {\r
8857     SaveGame(f, 0, "");\r
8858     fclose(f);\r
8859   }\r
8860 }\r
8861 \r
8862 \r
8863 void\r
8864 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8865 {\r
8866   if (delayedTimerEvent != 0) {\r
8867     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8868       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8869     }\r
8870     KillTimer(hwndMain, delayedTimerEvent);\r
8871     delayedTimerEvent = 0;\r
8872     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8873     delayedTimerCallback();\r
8874   }\r
8875   delayedTimerCallback = cb;\r
8876   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8877                                 (UINT) millisec, NULL);\r
8878 }\r
8879 \r
8880 DelayedEventCallback\r
8881 GetDelayedEvent()\r
8882 {\r
8883   if (delayedTimerEvent) {\r
8884     return delayedTimerCallback;\r
8885   } else {\r
8886     return NULL;\r
8887   }\r
8888 }\r
8889 \r
8890 void\r
8891 CancelDelayedEvent()\r
8892 {\r
8893   if (delayedTimerEvent) {\r
8894     KillTimer(hwndMain, delayedTimerEvent);\r
8895     delayedTimerEvent = 0;\r
8896   }\r
8897 }\r
8898 \r
8899 DWORD GetWin32Priority(int nice)\r
8900 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8901 /*\r
8902 REALTIME_PRIORITY_CLASS     0x00000100\r
8903 HIGH_PRIORITY_CLASS         0x00000080\r
8904 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8905 NORMAL_PRIORITY_CLASS       0x00000020\r
8906 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8907 IDLE_PRIORITY_CLASS         0x00000040\r
8908 */\r
8909         if (nice < -15) return 0x00000080;\r
8910         if (nice < 0)   return 0x00008000;\r
8911         if (nice == 0)  return 0x00000020;\r
8912         if (nice < 15)  return 0x00004000;\r
8913         return 0x00000040;\r
8914 }\r
8915 \r
8916 void RunCommand(char *cmdLine)\r
8917 {\r
8918   /* Now create the child process. */\r
8919   STARTUPINFO siStartInfo;\r
8920   PROCESS_INFORMATION piProcInfo;\r
8921 \r
8922   siStartInfo.cb = sizeof(STARTUPINFO);\r
8923   siStartInfo.lpReserved = NULL;\r
8924   siStartInfo.lpDesktop = NULL;\r
8925   siStartInfo.lpTitle = NULL;\r
8926   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8927   siStartInfo.cbReserved2 = 0;\r
8928   siStartInfo.lpReserved2 = NULL;\r
8929   siStartInfo.hStdInput = NULL;\r
8930   siStartInfo.hStdOutput = NULL;\r
8931   siStartInfo.hStdError = NULL;\r
8932 \r
8933   CreateProcess(NULL,\r
8934                 cmdLine,           /* command line */\r
8935                 NULL,      /* process security attributes */\r
8936                 NULL,      /* primary thread security attrs */\r
8937                 TRUE,      /* handles are inherited */\r
8938                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8939                 NULL,      /* use parent's environment */\r
8940                 NULL,\r
8941                 &siStartInfo, /* STARTUPINFO pointer */\r
8942                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8943 \r
8944   CloseHandle(piProcInfo.hThread);\r
8945 }\r
8946 \r
8947 /* Start a child process running the given program.\r
8948    The process's standard output can be read from "from", and its\r
8949    standard input can be written to "to".\r
8950    Exit with fatal error if anything goes wrong.\r
8951    Returns an opaque pointer that can be used to destroy the process\r
8952    later.\r
8953 */\r
8954 int\r
8955 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8956 {\r
8957 #define BUFSIZE 4096\r
8958 \r
8959   HANDLE hChildStdinRd, hChildStdinWr,\r
8960     hChildStdoutRd, hChildStdoutWr;\r
8961   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8962   SECURITY_ATTRIBUTES saAttr;\r
8963   BOOL fSuccess;\r
8964   PROCESS_INFORMATION piProcInfo;\r
8965   STARTUPINFO siStartInfo;\r
8966   ChildProc *cp;\r
8967   char buf[MSG_SIZ];\r
8968   DWORD err;\r
8969 \r
8970   if (appData.debugMode) {\r
8971     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8972   }\r
8973 \r
8974   *pr = NoProc;\r
8975 \r
8976   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8977   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8978   saAttr.bInheritHandle = TRUE;\r
8979   saAttr.lpSecurityDescriptor = NULL;\r
8980 \r
8981   /*\r
8982    * The steps for redirecting child's STDOUT:\r
8983    *     1. Create anonymous pipe to be STDOUT for child.\r
8984    *     2. Create a noninheritable duplicate of read handle,\r
8985    *         and close the inheritable read handle.\r
8986    */\r
8987 \r
8988   /* Create a pipe for the child's STDOUT. */\r
8989   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8990     return GetLastError();\r
8991   }\r
8992 \r
8993   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8994   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8995                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8996                              FALSE,     /* not inherited */\r
8997                              DUPLICATE_SAME_ACCESS);\r
8998   if (! fSuccess) {\r
8999     return GetLastError();\r
9000   }\r
9001   CloseHandle(hChildStdoutRd);\r
9002 \r
9003   /*\r
9004    * The steps for redirecting child's STDIN:\r
9005    *     1. Create anonymous pipe to be STDIN for child.\r
9006    *     2. Create a noninheritable duplicate of write handle,\r
9007    *         and close the inheritable write handle.\r
9008    */\r
9009 \r
9010   /* Create a pipe for the child's STDIN. */\r
9011   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9012     return GetLastError();\r
9013   }\r
9014 \r
9015   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9016   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9017                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9018                              FALSE,     /* not inherited */\r
9019                              DUPLICATE_SAME_ACCESS);\r
9020   if (! fSuccess) {\r
9021     return GetLastError();\r
9022   }\r
9023   CloseHandle(hChildStdinWr);\r
9024 \r
9025   /* Arrange to (1) look in dir for the child .exe file, and\r
9026    * (2) have dir be the child's working directory.  Interpret\r
9027    * dir relative to the directory WinBoard loaded from. */\r
9028   GetCurrentDirectory(MSG_SIZ, buf);\r
9029   SetCurrentDirectory(installDir);\r
9030   SetCurrentDirectory(dir);\r
9031 \r
9032   /* Now create the child process. */\r
9033 \r
9034   siStartInfo.cb = sizeof(STARTUPINFO);\r
9035   siStartInfo.lpReserved = NULL;\r
9036   siStartInfo.lpDesktop = NULL;\r
9037   siStartInfo.lpTitle = NULL;\r
9038   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9039   siStartInfo.cbReserved2 = 0;\r
9040   siStartInfo.lpReserved2 = NULL;\r
9041   siStartInfo.hStdInput = hChildStdinRd;\r
9042   siStartInfo.hStdOutput = hChildStdoutWr;\r
9043   siStartInfo.hStdError = hChildStdoutWr;\r
9044 \r
9045   fSuccess = CreateProcess(NULL,\r
9046                            cmdLine,        /* command line */\r
9047                            NULL,           /* process security attributes */\r
9048                            NULL,           /* primary thread security attrs */\r
9049                            TRUE,           /* handles are inherited */\r
9050                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9051                            NULL,           /* use parent's environment */\r
9052                            NULL,\r
9053                            &siStartInfo, /* STARTUPINFO pointer */\r
9054                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9055 \r
9056   err = GetLastError();\r
9057   SetCurrentDirectory(buf); /* return to prev directory */\r
9058   if (! fSuccess) {\r
9059     return err;\r
9060   }\r
9061 \r
9062   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9063     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9064     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9065   }\r
9066 \r
9067   /* Close the handles we don't need in the parent */\r
9068   CloseHandle(piProcInfo.hThread);\r
9069   CloseHandle(hChildStdinRd);\r
9070   CloseHandle(hChildStdoutWr);\r
9071 \r
9072   /* Prepare return value */\r
9073   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9074   cp->kind = CPReal;\r
9075   cp->hProcess = piProcInfo.hProcess;\r
9076   cp->pid = piProcInfo.dwProcessId;\r
9077   cp->hFrom = hChildStdoutRdDup;\r
9078   cp->hTo = hChildStdinWrDup;\r
9079 \r
9080   *pr = (void *) cp;\r
9081 \r
9082   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9083      2000 where engines sometimes don't see the initial command(s)\r
9084      from WinBoard and hang.  I don't understand how that can happen,\r
9085      but the Sleep is harmless, so I've put it in.  Others have also\r
9086      reported what may be the same problem, so hopefully this will fix\r
9087      it for them too.  */\r
9088   Sleep(500);\r
9089 \r
9090   return NO_ERROR;\r
9091 }\r
9092 \r
9093 \r
9094 void\r
9095 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9096 {\r
9097   ChildProc *cp; int result;\r
9098 \r
9099   cp = (ChildProc *) pr;\r
9100   if (cp == NULL) return;\r
9101 \r
9102   switch (cp->kind) {\r
9103   case CPReal:\r
9104     /* TerminateProcess is considered harmful, so... */\r
9105     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9106     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9107     /* The following doesn't work because the chess program\r
9108        doesn't "have the same console" as WinBoard.  Maybe\r
9109        we could arrange for this even though neither WinBoard\r
9110        nor the chess program uses a console for stdio? */\r
9111     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9112 \r
9113     /* [AS] Special termination modes for misbehaving programs... */\r
9114     if( signal == 9 ) { \r
9115         result = TerminateProcess( cp->hProcess, 0 );\r
9116 \r
9117         if ( appData.debugMode) {\r
9118             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9119         }\r
9120     }\r
9121     else if( signal == 10 ) {\r
9122         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9123 \r
9124         if( dw != WAIT_OBJECT_0 ) {\r
9125             result = TerminateProcess( cp->hProcess, 0 );\r
9126 \r
9127             if ( appData.debugMode) {\r
9128                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9129             }\r
9130 \r
9131         }\r
9132     }\r
9133 \r
9134     CloseHandle(cp->hProcess);\r
9135     break;\r
9136 \r
9137   case CPComm:\r
9138     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9139     break;\r
9140 \r
9141   case CPSock:\r
9142     closesocket(cp->sock);\r
9143     WSACleanup();\r
9144     break;\r
9145 \r
9146   case CPRcmd:\r
9147     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9148     closesocket(cp->sock);\r
9149     closesocket(cp->sock2);\r
9150     WSACleanup();\r
9151     break;\r
9152   }\r
9153   free(cp);\r
9154 }\r
9155 \r
9156 void\r
9157 InterruptChildProcess(ProcRef pr)\r
9158 {\r
9159   ChildProc *cp;\r
9160 \r
9161   cp = (ChildProc *) pr;\r
9162   if (cp == NULL) return;\r
9163   switch (cp->kind) {\r
9164   case CPReal:\r
9165     /* The following doesn't work because the chess program\r
9166        doesn't "have the same console" as WinBoard.  Maybe\r
9167        we could arrange for this even though neither WinBoard\r
9168        nor the chess program uses a console for stdio */\r
9169     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9170     break;\r
9171 \r
9172   case CPComm:\r
9173   case CPSock:\r
9174     /* Can't interrupt */\r
9175     break;\r
9176 \r
9177   case CPRcmd:\r
9178     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9179     break;\r
9180   }\r
9181 }\r
9182 \r
9183 \r
9184 int\r
9185 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9186 {\r
9187   char cmdLine[MSG_SIZ];\r
9188 \r
9189   if (port[0] == NULLCHAR) {\r
9190     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9191   } else {\r
9192     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9193   }\r
9194   return StartChildProcess(cmdLine, "", pr);\r
9195 }\r
9196 \r
9197 \r
9198 /* Code to open TCP sockets */\r
9199 \r
9200 int\r
9201 OpenTCP(char *host, char *port, ProcRef *pr)\r
9202 {\r
9203   ChildProc *cp;\r
9204   int err;\r
9205   SOCKET s;\r
9206   struct sockaddr_in sa, mysa;\r
9207   struct hostent FAR *hp;\r
9208   unsigned short uport;\r
9209   WORD wVersionRequested;\r
9210   WSADATA wsaData;\r
9211 \r
9212   /* Initialize socket DLL */\r
9213   wVersionRequested = MAKEWORD(1, 1);\r
9214   err = WSAStartup(wVersionRequested, &wsaData);\r
9215   if (err != 0) return err;\r
9216 \r
9217   /* Make socket */\r
9218   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9219     err = WSAGetLastError();\r
9220     WSACleanup();\r
9221     return err;\r
9222   }\r
9223 \r
9224   /* Bind local address using (mostly) don't-care values.\r
9225    */\r
9226   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9227   mysa.sin_family = AF_INET;\r
9228   mysa.sin_addr.s_addr = INADDR_ANY;\r
9229   uport = (unsigned short) 0;\r
9230   mysa.sin_port = htons(uport);\r
9231   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9232       == SOCKET_ERROR) {\r
9233     err = WSAGetLastError();\r
9234     WSACleanup();\r
9235     return err;\r
9236   }\r
9237 \r
9238   /* Resolve remote host name */\r
9239   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9240   if (!(hp = gethostbyname(host))) {\r
9241     unsigned int b0, b1, b2, b3;\r
9242 \r
9243     err = WSAGetLastError();\r
9244 \r
9245     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9246       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9247       hp->h_addrtype = AF_INET;\r
9248       hp->h_length = 4;\r
9249       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9250       hp->h_addr_list[0] = (char *) malloc(4);\r
9251       hp->h_addr_list[0][0] = (char) b0;\r
9252       hp->h_addr_list[0][1] = (char) b1;\r
9253       hp->h_addr_list[0][2] = (char) b2;\r
9254       hp->h_addr_list[0][3] = (char) b3;\r
9255     } else {\r
9256       WSACleanup();\r
9257       return err;\r
9258     }\r
9259   }\r
9260   sa.sin_family = hp->h_addrtype;\r
9261   uport = (unsigned short) atoi(port);\r
9262   sa.sin_port = htons(uport);\r
9263   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9264 \r
9265   /* Make connection */\r
9266   if (connect(s, (struct sockaddr *) &sa,\r
9267               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9268     err = WSAGetLastError();\r
9269     WSACleanup();\r
9270     return err;\r
9271   }\r
9272 \r
9273   /* Prepare return value */\r
9274   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9275   cp->kind = CPSock;\r
9276   cp->sock = s;\r
9277   *pr = (ProcRef *) cp;\r
9278 \r
9279   return NO_ERROR;\r
9280 }\r
9281 \r
9282 int\r
9283 OpenCommPort(char *name, ProcRef *pr)\r
9284 {\r
9285   HANDLE h;\r
9286   COMMTIMEOUTS ct;\r
9287   ChildProc *cp;\r
9288   char fullname[MSG_SIZ];\r
9289 \r
9290   if (*name != '\\')\r
9291     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9292   else\r
9293     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9294 \r
9295   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9296                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9297   if (h == (HANDLE) -1) {\r
9298     return GetLastError();\r
9299   }\r
9300   hCommPort = h;\r
9301 \r
9302   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9303 \r
9304   /* Accumulate characters until a 100ms pause, then parse */\r
9305   ct.ReadIntervalTimeout = 100;\r
9306   ct.ReadTotalTimeoutMultiplier = 0;\r
9307   ct.ReadTotalTimeoutConstant = 0;\r
9308   ct.WriteTotalTimeoutMultiplier = 0;\r
9309   ct.WriteTotalTimeoutConstant = 0;\r
9310   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9311 \r
9312   /* Prepare return value */\r
9313   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9314   cp->kind = CPComm;\r
9315   cp->hFrom = h;\r
9316   cp->hTo = h;\r
9317   *pr = (ProcRef *) cp;\r
9318 \r
9319   return NO_ERROR;\r
9320 }\r
9321 \r
9322 int\r
9323 OpenLoopback(ProcRef *pr)\r
9324 {\r
9325   DisplayFatalError(_("Not implemented"), 0, 1);\r
9326   return NO_ERROR;\r
9327 }\r
9328 \r
9329 \r
9330 int\r
9331 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9332 {\r
9333   ChildProc *cp;\r
9334   int err;\r
9335   SOCKET s, s2, s3;\r
9336   struct sockaddr_in sa, mysa;\r
9337   struct hostent FAR *hp;\r
9338   unsigned short uport;\r
9339   WORD wVersionRequested;\r
9340   WSADATA wsaData;\r
9341   int fromPort;\r
9342   char stderrPortStr[MSG_SIZ];\r
9343 \r
9344   /* Initialize socket DLL */\r
9345   wVersionRequested = MAKEWORD(1, 1);\r
9346   err = WSAStartup(wVersionRequested, &wsaData);\r
9347   if (err != 0) return err;\r
9348 \r
9349   /* Resolve remote host name */\r
9350   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9351   if (!(hp = gethostbyname(host))) {\r
9352     unsigned int b0, b1, b2, b3;\r
9353 \r
9354     err = WSAGetLastError();\r
9355 \r
9356     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9357       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9358       hp->h_addrtype = AF_INET;\r
9359       hp->h_length = 4;\r
9360       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9361       hp->h_addr_list[0] = (char *) malloc(4);\r
9362       hp->h_addr_list[0][0] = (char) b0;\r
9363       hp->h_addr_list[0][1] = (char) b1;\r
9364       hp->h_addr_list[0][2] = (char) b2;\r
9365       hp->h_addr_list[0][3] = (char) b3;\r
9366     } else {\r
9367       WSACleanup();\r
9368       return err;\r
9369     }\r
9370   }\r
9371   sa.sin_family = hp->h_addrtype;\r
9372   uport = (unsigned short) 514;\r
9373   sa.sin_port = htons(uport);\r
9374   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9375 \r
9376   /* Bind local socket to unused "privileged" port address\r
9377    */\r
9378   s = INVALID_SOCKET;\r
9379   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9380   mysa.sin_family = AF_INET;\r
9381   mysa.sin_addr.s_addr = INADDR_ANY;\r
9382   for (fromPort = 1023;; fromPort--) {\r
9383     if (fromPort < 0) {\r
9384       WSACleanup();\r
9385       return WSAEADDRINUSE;\r
9386     }\r
9387     if (s == INVALID_SOCKET) {\r
9388       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9389         err = WSAGetLastError();\r
9390         WSACleanup();\r
9391         return err;\r
9392       }\r
9393     }\r
9394     uport = (unsigned short) fromPort;\r
9395     mysa.sin_port = htons(uport);\r
9396     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9397         == SOCKET_ERROR) {\r
9398       err = WSAGetLastError();\r
9399       if (err == WSAEADDRINUSE) continue;\r
9400       WSACleanup();\r
9401       return err;\r
9402     }\r
9403     if (connect(s, (struct sockaddr *) &sa,\r
9404       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9405       err = WSAGetLastError();\r
9406       if (err == WSAEADDRINUSE) {\r
9407         closesocket(s);\r
9408         s = -1;\r
9409         continue;\r
9410       }\r
9411       WSACleanup();\r
9412       return err;\r
9413     }\r
9414     break;\r
9415   }\r
9416 \r
9417   /* Bind stderr local socket to unused "privileged" port address\r
9418    */\r
9419   s2 = INVALID_SOCKET;\r
9420   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9421   mysa.sin_family = AF_INET;\r
9422   mysa.sin_addr.s_addr = INADDR_ANY;\r
9423   for (fromPort = 1023;; fromPort--) {\r
9424     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9425     if (fromPort < 0) {\r
9426       (void) closesocket(s);\r
9427       WSACleanup();\r
9428       return WSAEADDRINUSE;\r
9429     }\r
9430     if (s2 == INVALID_SOCKET) {\r
9431       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9432         err = WSAGetLastError();\r
9433         closesocket(s);\r
9434         WSACleanup();\r
9435         return err;\r
9436       }\r
9437     }\r
9438     uport = (unsigned short) fromPort;\r
9439     mysa.sin_port = htons(uport);\r
9440     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9441         == SOCKET_ERROR) {\r
9442       err = WSAGetLastError();\r
9443       if (err == WSAEADDRINUSE) continue;\r
9444       (void) closesocket(s);\r
9445       WSACleanup();\r
9446       return err;\r
9447     }\r
9448     if (listen(s2, 1) == SOCKET_ERROR) {\r
9449       err = WSAGetLastError();\r
9450       if (err == WSAEADDRINUSE) {\r
9451         closesocket(s2);\r
9452         s2 = INVALID_SOCKET;\r
9453         continue;\r
9454       }\r
9455       (void) closesocket(s);\r
9456       (void) closesocket(s2);\r
9457       WSACleanup();\r
9458       return err;\r
9459     }\r
9460     break;\r
9461   }\r
9462   prevStderrPort = fromPort; // remember port used\r
9463   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9464 \r
9465   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9466     err = WSAGetLastError();\r
9467     (void) closesocket(s);\r
9468     (void) closesocket(s2);\r
9469     WSACleanup();\r
9470     return err;\r
9471   }\r
9472 \r
9473   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9474     err = WSAGetLastError();\r
9475     (void) closesocket(s);\r
9476     (void) closesocket(s2);\r
9477     WSACleanup();\r
9478     return err;\r
9479   }\r
9480   if (*user == NULLCHAR) user = UserName();\r
9481   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9482     err = WSAGetLastError();\r
9483     (void) closesocket(s);\r
9484     (void) closesocket(s2);\r
9485     WSACleanup();\r
9486     return err;\r
9487   }\r
9488   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9489     err = WSAGetLastError();\r
9490     (void) closesocket(s);\r
9491     (void) closesocket(s2);\r
9492     WSACleanup();\r
9493     return err;\r
9494   }\r
9495 \r
9496   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9497     err = WSAGetLastError();\r
9498     (void) closesocket(s);\r
9499     (void) closesocket(s2);\r
9500     WSACleanup();\r
9501     return err;\r
9502   }\r
9503   (void) closesocket(s2);  /* Stop listening */\r
9504 \r
9505   /* Prepare return value */\r
9506   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9507   cp->kind = CPRcmd;\r
9508   cp->sock = s;\r
9509   cp->sock2 = s3;\r
9510   *pr = (ProcRef *) cp;\r
9511 \r
9512   return NO_ERROR;\r
9513 }\r
9514 \r
9515 \r
9516 InputSourceRef\r
9517 AddInputSource(ProcRef pr, int lineByLine,\r
9518                InputCallback func, VOIDSTAR closure)\r
9519 {\r
9520   InputSource *is, *is2 = NULL;\r
9521   ChildProc *cp = (ChildProc *) pr;\r
9522 \r
9523   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9524   is->lineByLine = lineByLine;\r
9525   is->func = func;\r
9526   is->closure = closure;\r
9527   is->second = NULL;\r
9528   is->next = is->buf;\r
9529   if (pr == NoProc) {\r
9530     is->kind = CPReal;\r
9531     consoleInputSource = is;\r
9532   } else {\r
9533     is->kind = cp->kind;\r
9534     /* \r
9535         [AS] Try to avoid a race condition if the thread is given control too early:\r
9536         we create all threads suspended so that the is->hThread variable can be\r
9537         safely assigned, then let the threads start with ResumeThread.\r
9538     */\r
9539     switch (cp->kind) {\r
9540     case CPReal:\r
9541       is->hFile = cp->hFrom;\r
9542       cp->hFrom = NULL; /* now owned by InputThread */\r
9543       is->hThread =\r
9544         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9545                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9546       break;\r
9547 \r
9548     case CPComm:\r
9549       is->hFile = cp->hFrom;\r
9550       cp->hFrom = NULL; /* now owned by InputThread */\r
9551       is->hThread =\r
9552         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9553                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9554       break;\r
9555 \r
9556     case CPSock:\r
9557       is->sock = cp->sock;\r
9558       is->hThread =\r
9559         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9560                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9561       break;\r
9562 \r
9563     case CPRcmd:\r
9564       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9565       *is2 = *is;\r
9566       is->sock = cp->sock;\r
9567       is->second = is2;\r
9568       is2->sock = cp->sock2;\r
9569       is2->second = is2;\r
9570       is->hThread =\r
9571         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9572                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9573       is2->hThread =\r
9574         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9575                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9576       break;\r
9577     }\r
9578 \r
9579     if( is->hThread != NULL ) {\r
9580         ResumeThread( is->hThread );\r
9581     }\r
9582 \r
9583     if( is2 != NULL && is2->hThread != NULL ) {\r
9584         ResumeThread( is2->hThread );\r
9585     }\r
9586   }\r
9587 \r
9588   return (InputSourceRef) is;\r
9589 }\r
9590 \r
9591 void\r
9592 RemoveInputSource(InputSourceRef isr)\r
9593 {\r
9594   InputSource *is;\r
9595 \r
9596   is = (InputSource *) isr;\r
9597   is->hThread = NULL;  /* tell thread to stop */\r
9598   CloseHandle(is->hThread);\r
9599   if (is->second != NULL) {\r
9600     is->second->hThread = NULL;\r
9601     CloseHandle(is->second->hThread);\r
9602   }\r
9603 }\r
9604 \r
9605 int no_wrap(char *message, int count)\r
9606 {\r
9607     ConsoleOutput(message, count, FALSE);\r
9608     return count;\r
9609 }\r
9610 \r
9611 int\r
9612 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9613 {\r
9614   DWORD dOutCount;\r
9615   int outCount = SOCKET_ERROR;\r
9616   ChildProc *cp = (ChildProc *) pr;\r
9617   static OVERLAPPED ovl;\r
9618   static int line = 0;\r
9619 \r
9620   if (pr == NoProc)\r
9621   {\r
9622     if (appData.noJoin || !appData.useInternalWrap)\r
9623       return no_wrap(message, count);\r
9624     else\r
9625     {\r
9626       int width = get_term_width();\r
9627       int len = wrap(NULL, message, count, width, &line);\r
9628       char *msg = malloc(len);\r
9629       int dbgchk;\r
9630 \r
9631       if (!msg)\r
9632         return no_wrap(message, count);\r
9633       else\r
9634       {\r
9635         dbgchk = wrap(msg, message, count, width, &line);\r
9636         if (dbgchk != len && appData.debugMode)\r
9637             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9638         ConsoleOutput(msg, len, FALSE);\r
9639         free(msg);\r
9640         return len;\r
9641       }\r
9642     }\r
9643   }\r
9644 \r
9645   if (ovl.hEvent == NULL) {\r
9646     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9647   }\r
9648   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9649 \r
9650   switch (cp->kind) {\r
9651   case CPSock:\r
9652   case CPRcmd:\r
9653     outCount = send(cp->sock, message, count, 0);\r
9654     if (outCount == SOCKET_ERROR) {\r
9655       *outError = WSAGetLastError();\r
9656     } else {\r
9657       *outError = NO_ERROR;\r
9658     }\r
9659     break;\r
9660 \r
9661   case CPReal:\r
9662     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9663                   &dOutCount, NULL)) {\r
9664       *outError = NO_ERROR;\r
9665       outCount = (int) dOutCount;\r
9666     } else {\r
9667       *outError = GetLastError();\r
9668     }\r
9669     break;\r
9670 \r
9671   case CPComm:\r
9672     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9673                             &dOutCount, &ovl);\r
9674     if (*outError == NO_ERROR) {\r
9675       outCount = (int) dOutCount;\r
9676     }\r
9677     break;\r
9678   }\r
9679   return outCount;\r
9680 }\r
9681 \r
9682 void\r
9683 DoSleep(int n)\r
9684 {\r
9685     if(n != 0) Sleep(n);\r
9686 }\r
9687 \r
9688 int\r
9689 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9690                        long msdelay)\r
9691 {\r
9692   /* Ignore delay, not implemented for WinBoard */\r
9693   return OutputToProcess(pr, message, count, outError);\r
9694 }\r
9695 \r
9696 \r
9697 void\r
9698 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9699                         char *buf, int count, int error)\r
9700 {\r
9701   DisplayFatalError(_("Not implemented"), 0, 1);\r
9702 }\r
9703 \r
9704 /* see wgamelist.c for Game List functions */\r
9705 /* see wedittags.c for Edit Tags functions */\r
9706 \r
9707 \r
9708 VOID\r
9709 ICSInitScript()\r
9710 {\r
9711   FILE *f;\r
9712   char buf[MSG_SIZ];\r
9713   char *dummy;\r
9714 \r
9715   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9716     f = fopen(buf, "r");\r
9717     if (f != NULL) {\r
9718       ProcessICSInitScript(f);\r
9719       fclose(f);\r
9720     }\r
9721   }\r
9722 }\r
9723 \r
9724 \r
9725 VOID\r
9726 StartAnalysisClock()\r
9727 {\r
9728   if (analysisTimerEvent) return;\r
9729   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9730                                         (UINT) 2000, NULL);\r
9731 }\r
9732 \r
9733 VOID\r
9734 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9735 {\r
9736   highlightInfo.sq[0].x = fromX;\r
9737   highlightInfo.sq[0].y = fromY;\r
9738   highlightInfo.sq[1].x = toX;\r
9739   highlightInfo.sq[1].y = toY;\r
9740 }\r
9741 \r
9742 VOID\r
9743 ClearHighlights()\r
9744 {\r
9745   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9746     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9747 }\r
9748 \r
9749 VOID\r
9750 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9751 {\r
9752   premoveHighlightInfo.sq[0].x = fromX;\r
9753   premoveHighlightInfo.sq[0].y = fromY;\r
9754   premoveHighlightInfo.sq[1].x = toX;\r
9755   premoveHighlightInfo.sq[1].y = toY;\r
9756 }\r
9757 \r
9758 VOID\r
9759 ClearPremoveHighlights()\r
9760 {\r
9761   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9762     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9763 }\r
9764 \r
9765 VOID\r
9766 ShutDownFrontEnd()\r
9767 {\r
9768   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9769   DeleteClipboardTempFiles();\r
9770 }\r
9771 \r
9772 void\r
9773 BoardToTop()\r
9774 {\r
9775     if (IsIconic(hwndMain))\r
9776       ShowWindow(hwndMain, SW_RESTORE);\r
9777 \r
9778     SetActiveWindow(hwndMain);\r
9779 }\r
9780 \r
9781 /*\r
9782  * Prototypes for animation support routines\r
9783  */\r
9784 static void ScreenSquare(int column, int row, POINT * pt);\r
9785 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9786      POINT frames[], int * nFrames);\r
9787 \r
9788 \r
9789 #define kFactor 4\r
9790 \r
9791 void\r
9792 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9793 {       // [HGM] atomic: animate blast wave\r
9794         int i;\r
9795 \r
9796         explodeInfo.fromX = fromX;\r
9797         explodeInfo.fromY = fromY;\r
9798         explodeInfo.toX = toX;\r
9799         explodeInfo.toY = toY;\r
9800         for(i=1; i<4*kFactor; i++) {\r
9801             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9802             DrawPosition(FALSE, board);\r
9803             Sleep(appData.animSpeed);\r
9804         }\r
9805         explodeInfo.radius = 0;\r
9806         DrawPosition(TRUE, board);\r
9807 }\r
9808 \r
9809 void\r
9810 AnimateMove(board, fromX, fromY, toX, toY)\r
9811      Board board;\r
9812      int fromX;\r
9813      int fromY;\r
9814      int toX;\r
9815      int toY;\r
9816 {\r
9817   ChessSquare piece;\r
9818   POINT start, finish, mid;\r
9819   POINT frames[kFactor * 2 + 1];\r
9820   int nFrames, n;\r
9821 \r
9822   if (!appData.animate) return;\r
9823   if (doingSizing) return;\r
9824   if (fromY < 0 || fromX < 0) return;\r
9825   piece = board[fromY][fromX];\r
9826   if (piece >= EmptySquare) return;\r
9827 \r
9828   ScreenSquare(fromX, fromY, &start);\r
9829   ScreenSquare(toX, toY, &finish);\r
9830 \r
9831   /* All moves except knight jumps move in straight line */\r
9832   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9833     mid.x = start.x + (finish.x - start.x) / 2;\r
9834     mid.y = start.y + (finish.y - start.y) / 2;\r
9835   } else {\r
9836     /* Knight: make straight movement then diagonal */\r
9837     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9838        mid.x = start.x + (finish.x - start.x) / 2;\r
9839        mid.y = start.y;\r
9840      } else {\r
9841        mid.x = start.x;\r
9842        mid.y = start.y + (finish.y - start.y) / 2;\r
9843      }\r
9844   }\r
9845   \r
9846   /* Don't use as many frames for very short moves */\r
9847   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9848     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9849   else\r
9850     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9851 \r
9852   animInfo.from.x = fromX;\r
9853   animInfo.from.y = fromY;\r
9854   animInfo.to.x = toX;\r
9855   animInfo.to.y = toY;\r
9856   animInfo.lastpos = start;\r
9857   animInfo.piece = piece;\r
9858   for (n = 0; n < nFrames; n++) {\r
9859     animInfo.pos = frames[n];\r
9860     DrawPosition(FALSE, NULL);\r
9861     animInfo.lastpos = animInfo.pos;\r
9862     Sleep(appData.animSpeed);\r
9863   }\r
9864   animInfo.pos = finish;\r
9865   DrawPosition(FALSE, NULL);\r
9866   animInfo.piece = EmptySquare;\r
9867   Explode(board, fromX, fromY, toX, toY);\r
9868 }\r
9869 \r
9870 /*      Convert board position to corner of screen rect and color       */\r
9871 \r
9872 static void\r
9873 ScreenSquare(column, row, pt)\r
9874      int column; int row; POINT * pt;\r
9875 {\r
9876   if (flipView) {\r
9877     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9878     pt->y = lineGap + row * (squareSize + lineGap);\r
9879   } else {\r
9880     pt->x = lineGap + column * (squareSize + lineGap);\r
9881     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9882   }\r
9883 }\r
9884 \r
9885 /*      Generate a series of frame coords from start->mid->finish.\r
9886         The movement rate doubles until the half way point is\r
9887         reached, then halves back down to the final destination,\r
9888         which gives a nice slow in/out effect. The algorithmn\r
9889         may seem to generate too many intermediates for short\r
9890         moves, but remember that the purpose is to attract the\r
9891         viewers attention to the piece about to be moved and\r
9892         then to where it ends up. Too few frames would be less\r
9893         noticeable.                                             */\r
9894 \r
9895 static void\r
9896 Tween(start, mid, finish, factor, frames, nFrames)\r
9897      POINT * start; POINT * mid;\r
9898      POINT * finish; int factor;\r
9899      POINT frames[]; int * nFrames;\r
9900 {\r
9901   int n, fraction = 1, count = 0;\r
9902 \r
9903   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9904   for (n = 0; n < factor; n++)\r
9905     fraction *= 2;\r
9906   for (n = 0; n < factor; n++) {\r
9907     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9908     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9909     count ++;\r
9910     fraction = fraction / 2;\r
9911   }\r
9912   \r
9913   /* Midpoint */\r
9914   frames[count] = *mid;\r
9915   count ++;\r
9916   \r
9917   /* Slow out, stepping 1/2, then 1/4, ... */\r
9918   fraction = 2;\r
9919   for (n = 0; n < factor; n++) {\r
9920     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9921     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9922     count ++;\r
9923     fraction = fraction * 2;\r
9924   }\r
9925   *nFrames = count;\r
9926 }\r
9927 \r
9928 void\r
9929 SettingsPopUp(ChessProgramState *cps)\r
9930 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9931       EngineOptionsPopup(savedHwnd, cps);\r
9932 }\r
9933 \r
9934 int flock(int fid, int code)\r
9935 {\r
9936     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9937     OVERLAPPED ov;\r
9938     ov.hEvent = NULL;\r
9939     ov.Offset = 0;\r
9940     ov.OffsetHigh = 0;\r
9941     switch(code) {\r
9942       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9943       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9944       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9945       default: return -1;\r
9946     }\r
9947     return 0;\r
9948 }\r