Move ICS-engine analyze and AnalyzeGame code to shared back-end
[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 enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
557   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
558   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
559   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
560   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
561   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1010         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1011       } else\r
1012       if(appData.directory[n] && appData.directory[n][0]) {\r
1013         SetCurrentDirectory(appData.directory[n]);\r
1014         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1015       }\r
1016   }\r
1017   SetCurrentDirectory(dir); /* return to prev directory */\r
1018 }\r
1019 \r
1020 VOID\r
1021 InitTextures()\r
1022 {\r
1023   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1024   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1025   \r
1026   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1027       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1028       liteBackTextureMode = appData.liteBackTextureMode;\r
1029 \r
1030       if (liteBackTexture == NULL && appData.debugMode) {\r
1031           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1032       }\r
1033   }\r
1034   \r
1035   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1036       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       darkBackTextureMode = appData.darkBackTextureMode;\r
1038 \r
1039       if (darkBackTexture == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1041       }\r
1042   }\r
1043 }\r
1044 \r
1045 BOOL\r
1046 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1047 {\r
1048   HWND hwnd; /* Main window handle. */\r
1049   int ibs;\r
1050   WINDOWPLACEMENT wp;\r
1051   char *filepart;\r
1052 \r
1053   hInst = hInstance;    /* Store instance handle in our global variable */\r
1054   programName = szAppName;\r
1055 \r
1056   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1057     *filepart = NULLCHAR;\r
1058   } else {\r
1059     GetCurrentDirectory(MSG_SIZ, installDir);\r
1060   }\r
1061   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1062   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1063   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1064   /* xboard, and older WinBoards, controlled the move sound with the\r
1065      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1066      always turn the option on (so that the backend will call us),\r
1067      then let the user turn the sound off by setting it to silence if\r
1068      desired.  To accommodate old winboard.ini files saved by old\r
1069      versions of WinBoard, we also turn off the sound if the option\r
1070      was initially set to false. [HGM] taken out of InitAppData */\r
1071   if (!appData.ringBellAfterMoves) {\r
1072     sounds[(int)SoundMove].name = strdup("");\r
1073     appData.ringBellAfterMoves = TRUE;\r
1074   }\r
1075   if (appData.debugMode) {\r
1076     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1077     setbuf(debugFP, NULL);\r
1078   }\r
1079 \r
1080   LoadLanguageFile(appData.language);\r
1081 \r
1082   InitBackEnd1();\r
1083 \r
1084 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1085 //  InitEngineUCI( installDir, &second );\r
1086 \r
1087   /* Create a main window for this application instance. */\r
1088   hwnd = CreateWindow(szAppName, szTitle,\r
1089                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1090                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1091                       NULL, NULL, hInstance, NULL);\r
1092   hwndMain = hwnd;\r
1093 \r
1094   /* If window could not be created, return "failure" */\r
1095   if (!hwnd) {\r
1096     return (FALSE);\r
1097   }\r
1098 \r
1099   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1100   LoadLogo(&first, 0, FALSE);\r
1101   LoadLogo(&second, 1, appData.icsActive);\r
1102 \r
1103   SetUserLogo();\r
1104 \r
1105   iconWhite = LoadIcon(hInstance, "icon_white");\r
1106   iconBlack = LoadIcon(hInstance, "icon_black");\r
1107   iconCurrent = iconWhite;\r
1108   InitDrawingColors();\r
1109   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1110   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1111   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1112     /* Compute window size for each board size, and use the largest\r
1113        size that fits on this screen as the default. */\r
1114     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1115     if (boardSize == (BoardSize)-1 &&\r
1116         winH <= screenHeight\r
1117            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1118         && winW <= screenWidth) {\r
1119       boardSize = (BoardSize)ibs;\r
1120     }\r
1121   }\r
1122 \r
1123   InitDrawingSizes(boardSize, 0);\r
1124   RecentEngineMenu(appData.recentEngineList);\r
1125   InitMenuChecks();\r
1126   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1127 \r
1128   /* [AS] Load textures if specified */\r
1129   InitTextures();\r
1130 \r
1131   mysrandom( (unsigned) time(NULL) );\r
1132 \r
1133   /* [AS] Restore layout */\r
1134   if( wpMoveHistory.visible ) {\r
1135       MoveHistoryPopUp();\r
1136   }\r
1137 \r
1138   if( wpEvalGraph.visible ) {\r
1139       EvalGraphPopUp();\r
1140   }\r
1141 \r
1142   if( wpEngineOutput.visible ) {\r
1143       EngineOutputPopUp();\r
1144   }\r
1145 \r
1146   /* Make the window visible; update its client area; and return "success" */\r
1147   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1148   wp.length = sizeof(WINDOWPLACEMENT);\r
1149   wp.flags = 0;\r
1150   wp.showCmd = nCmdShow;\r
1151   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1152   wp.rcNormalPosition.left = wpMain.x;\r
1153   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1154   wp.rcNormalPosition.top = wpMain.y;\r
1155   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1156   SetWindowPlacement(hwndMain, &wp);\r
1157 \r
1158   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1159 \r
1160   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1161                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1162 \r
1163   if (hwndConsole) {\r
1164 #if AOT_CONSOLE\r
1165     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1166                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1167 #endif\r
1168     ShowWindow(hwndConsole, nCmdShow);\r
1169     SetActiveWindow(hwndConsole);\r
1170   }\r
1171   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1172   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1173 \r
1174   return TRUE;\r
1175 \r
1176 }\r
1177 \r
1178 VOID\r
1179 InitMenuChecks()\r
1180 {\r
1181   HMENU hmenu = GetMenu(hwndMain);\r
1182 \r
1183   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1184                         MF_BYCOMMAND|((appData.icsActive &&\r
1185                                        *appData.icsCommPort != NULLCHAR) ?\r
1186                                       MF_ENABLED : MF_GRAYED));\r
1187   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1188                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1189                                      MF_CHECKED : MF_UNCHECKED));\r
1190 }\r
1191 \r
1192 //---------------------------------------------------------------------------------------------------------\r
1193 \r
1194 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1195 #define XBOARD FALSE\r
1196 \r
1197 #define OPTCHAR "/"\r
1198 #define SEPCHAR "="\r
1199 #define TOPLEVEL 0\r
1200 \r
1201 #include "args.h"\r
1202 \r
1203 // front-end part of option handling\r
1204 \r
1205 VOID\r
1206 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1207 {\r
1208   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1209   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1210   DeleteDC(hdc);\r
1211   lf->lfWidth = 0;\r
1212   lf->lfEscapement = 0;\r
1213   lf->lfOrientation = 0;\r
1214   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1215   lf->lfItalic = mfp->italic;\r
1216   lf->lfUnderline = mfp->underline;\r
1217   lf->lfStrikeOut = mfp->strikeout;\r
1218   lf->lfCharSet = mfp->charset;\r
1219   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1220   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1221   lf->lfQuality = DEFAULT_QUALITY;\r
1222   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1223     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1224 }\r
1225 \r
1226 void\r
1227 CreateFontInMF(MyFont *mf)\r
1228\r
1229   LFfromMFP(&mf->lf, &mf->mfp);\r
1230   if (mf->hf) DeleteObject(mf->hf);\r
1231   mf->hf = CreateFontIndirect(&mf->lf);\r
1232 }\r
1233 \r
1234 // [HGM] This platform-dependent table provides the location for storing the color info\r
1235 void *\r
1236 colorVariable[] = {\r
1237   &whitePieceColor, \r
1238   &blackPieceColor, \r
1239   &lightSquareColor,\r
1240   &darkSquareColor, \r
1241   &highlightSquareColor,\r
1242   &premoveHighlightColor,\r
1243   NULL,\r
1244   &consoleBackgroundColor,\r
1245   &appData.fontForeColorWhite,\r
1246   &appData.fontBackColorWhite,\r
1247   &appData.fontForeColorBlack,\r
1248   &appData.fontBackColorBlack,\r
1249   &appData.evalHistColorWhite,\r
1250   &appData.evalHistColorBlack,\r
1251   &appData.highlightArrowColor,\r
1252 };\r
1253 \r
1254 /* Command line font name parser.  NULL name means do nothing.\r
1255    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1256    For backward compatibility, syntax without the colon is also\r
1257    accepted, but font names with digits in them won't work in that case.\r
1258 */\r
1259 VOID\r
1260 ParseFontName(char *name, MyFontParams *mfp)\r
1261 {\r
1262   char *p, *q;\r
1263   if (name == NULL) return;\r
1264   p = name;\r
1265   q = strchr(p, ':');\r
1266   if (q) {\r
1267     if (q - p >= sizeof(mfp->faceName))\r
1268       ExitArgError(_("Font name too long:"), name, TRUE);\r
1269     memcpy(mfp->faceName, p, q - p);\r
1270     mfp->faceName[q - p] = NULLCHAR;\r
1271     p = q + 1;\r
1272   } else {\r
1273     q = mfp->faceName;\r
1274     while (*p && !isdigit(*p)) {\r
1275       *q++ = *p++;\r
1276       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1277         ExitArgError(_("Font name too long:"), name, TRUE);\r
1278     }\r
1279     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1280     *q = NULLCHAR;\r
1281   }\r
1282   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1283   mfp->pointSize = (float) atof(p);\r
1284   mfp->bold = (strchr(p, 'b') != NULL);\r
1285   mfp->italic = (strchr(p, 'i') != NULL);\r
1286   mfp->underline = (strchr(p, 'u') != NULL);\r
1287   mfp->strikeout = (strchr(p, 's') != NULL);\r
1288   mfp->charset = DEFAULT_CHARSET;\r
1289   q = strchr(p, 'c');\r
1290   if (q)\r
1291     mfp->charset = (BYTE) atoi(q+1);\r
1292 }\r
1293 \r
1294 void\r
1295 ParseFont(char *name, int number)\r
1296 { // wrapper to shield back-end from 'font'\r
1297   ParseFontName(name, &font[boardSize][number]->mfp);\r
1298 }\r
1299 \r
1300 void\r
1301 SetFontDefaults()\r
1302 { // in WB  we have a 2D array of fonts; this initializes their description\r
1303   int i, j;\r
1304   /* Point font array elements to structures and\r
1305      parse default font names */\r
1306   for (i=0; i<NUM_FONTS; i++) {\r
1307     for (j=0; j<NUM_SIZES; j++) {\r
1308       font[j][i] = &fontRec[j][i];\r
1309       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1310     }\r
1311   }\r
1312 }\r
1313 \r
1314 void\r
1315 CreateFonts()\r
1316 { // here we create the actual fonts from the selected descriptions\r
1317   int i, j;\r
1318   for (i=0; i<NUM_FONTS; i++) {\r
1319     for (j=0; j<NUM_SIZES; j++) {\r
1320       CreateFontInMF(font[j][i]);\r
1321     }\r
1322   }\r
1323 }\r
1324 /* Color name parser.\r
1325    X version accepts X color names, but this one\r
1326    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1327 COLORREF\r
1328 ParseColorName(char *name)\r
1329 {\r
1330   int red, green, blue, count;\r
1331   char buf[MSG_SIZ];\r
1332 \r
1333   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1334   if (count != 3) {\r
1335     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1336       &red, &green, &blue);\r
1337   }\r
1338   if (count != 3) {\r
1339     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1340     DisplayError(buf, 0);\r
1341     return RGB(0, 0, 0);\r
1342   }\r
1343   return PALETTERGB(red, green, blue);\r
1344 }\r
1345 \r
1346 void\r
1347 ParseColor(int n, char *name)\r
1348 { // for WinBoard the color is an int, which needs to be derived from the string\r
1349   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1354 {\r
1355   char *e = argValue;\r
1356   int eff = 0;\r
1357 \r
1358   while (*e) {\r
1359     if (*e == 'b')      eff |= CFE_BOLD;\r
1360     else if (*e == 'i') eff |= CFE_ITALIC;\r
1361     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1362     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1363     else if (*e == '#' || isdigit(*e)) break;\r
1364     e++;\r
1365   }\r
1366   *effects = eff;\r
1367   *color   = ParseColorName(e);\r
1368 }\r
1369 \r
1370 void\r
1371 ParseTextAttribs(ColorClass cc, char *s)\r
1372 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1373     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1374     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1375 }\r
1376 \r
1377 void\r
1378 ParseBoardSize(void *addr, char *name)\r
1379 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1380   BoardSize bs = SizeTiny;\r
1381   while (sizeInfo[bs].name != NULL) {\r
1382     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1383         *(BoardSize *)addr = bs;\r
1384         return;\r
1385     }\r
1386     bs++;\r
1387   }\r
1388   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1389 }\r
1390 \r
1391 void\r
1392 LoadAllSounds()\r
1393 { // [HGM] import name from appData first\r
1394   ColorClass cc;\r
1395   SoundClass sc;\r
1396   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1397     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1398     textAttribs[cc].sound.data = NULL;\r
1399     MyLoadSound(&textAttribs[cc].sound);\r
1400   }\r
1401   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1402     textAttribs[cc].sound.name = strdup("");\r
1403     textAttribs[cc].sound.data = NULL;\r
1404   }\r
1405   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1406     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1407     sounds[sc].data = NULL;\r
1408     MyLoadSound(&sounds[sc]);\r
1409   }\r
1410 }\r
1411 \r
1412 void\r
1413 SetCommPortDefaults()\r
1414 {\r
1415    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1416   dcb.DCBlength = sizeof(DCB);\r
1417   dcb.BaudRate = 9600;\r
1418   dcb.fBinary = TRUE;\r
1419   dcb.fParity = FALSE;\r
1420   dcb.fOutxCtsFlow = FALSE;\r
1421   dcb.fOutxDsrFlow = FALSE;\r
1422   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1423   dcb.fDsrSensitivity = FALSE;\r
1424   dcb.fTXContinueOnXoff = TRUE;\r
1425   dcb.fOutX = FALSE;\r
1426   dcb.fInX = FALSE;\r
1427   dcb.fNull = FALSE;\r
1428   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1429   dcb.fAbortOnError = FALSE;\r
1430   dcb.ByteSize = 7;\r
1431   dcb.Parity = SPACEPARITY;\r
1432   dcb.StopBits = ONESTOPBIT;\r
1433 }\r
1434 \r
1435 // [HGM] args: these three cases taken out to stay in front-end\r
1436 void\r
1437 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1438 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1439         // while the curent board size determines the element. This system should be ported to XBoard.\r
1440         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1441         int bs;\r
1442         for (bs=0; bs<NUM_SIZES; bs++) {\r
1443           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1444           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1445           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1446             ad->argName, mfp->faceName, mfp->pointSize,\r
1447             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1448             mfp->bold ? "b" : "",\r
1449             mfp->italic ? "i" : "",\r
1450             mfp->underline ? "u" : "",\r
1451             mfp->strikeout ? "s" : "",\r
1452             (int)mfp->charset);\r
1453         }\r
1454       }\r
1455 \r
1456 void\r
1457 ExportSounds()\r
1458 { // [HGM] copy the names from the internal WB variables to appData\r
1459   ColorClass cc;\r
1460   SoundClass sc;\r
1461   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1462     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1463   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1464     (&appData.soundMove)[sc] = sounds[sc].name;\r
1465 }\r
1466 \r
1467 void\r
1468 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1469 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1470         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1471         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1472           (ta->effects & CFE_BOLD) ? "b" : "",\r
1473           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1474           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1475           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1476           (ta->effects) ? " " : "",\r
1477           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1478       }\r
1479 \r
1480 void\r
1481 SaveColor(FILE *f, ArgDescriptor *ad)\r
1482 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1483         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1484         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1485           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1486 }\r
1487 \r
1488 void\r
1489 SaveBoardSize(FILE *f, char *name, void *addr)\r
1490 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1491   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1492 }\r
1493 \r
1494 void\r
1495 ParseCommPortSettings(char *s)\r
1496 { // wrapper to keep dcb from back-end\r
1497   ParseCommSettings(s, &dcb);\r
1498 }\r
1499 \r
1500 void\r
1501 GetWindowCoords()\r
1502 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1503   GetActualPlacement(hwndMain, &wpMain);\r
1504   GetActualPlacement(hwndConsole, &wpConsole);\r
1505   GetActualPlacement(commentDialog, &wpComment);\r
1506   GetActualPlacement(editTagsDialog, &wpTags);\r
1507   GetActualPlacement(gameListDialog, &wpGameList);\r
1508   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1509   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1510   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1511 }\r
1512 \r
1513 void\r
1514 PrintCommPortSettings(FILE *f, char *name)\r
1515 { // wrapper to shield back-end from DCB\r
1516       PrintCommSettings(f, name, &dcb);\r
1517 }\r
1518 \r
1519 int\r
1520 MySearchPath(char *installDir, char *name, char *fullname)\r
1521 {\r
1522   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1523   if(name[0]== '%') {\r
1524     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1525     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1526       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1527       *strchr(buf, '%') = 0;\r
1528       strcat(fullname, getenv(buf));\r
1529       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1530     }\r
1531     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1532     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1533     return (int) strlen(fullname);\r
1534   }\r
1535   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1536 }\r
1537 \r
1538 int\r
1539 MyGetFullPathName(char *name, char *fullname)\r
1540 {\r
1541   char *dummy;\r
1542   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1543 }\r
1544 \r
1545 int\r
1546 MainWindowUp()\r
1547 { // [HGM] args: allows testing if main window is realized from back-end\r
1548   return hwndMain != NULL;\r
1549 }\r
1550 \r
1551 void\r
1552 PopUpStartupDialog()\r
1553 {\r
1554     FARPROC lpProc;\r
1555     \r
1556     LoadLanguageFile(appData.language);\r
1557     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1558     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1559     FreeProcInstance(lpProc);\r
1560 }\r
1561 \r
1562 /*---------------------------------------------------------------------------*\\r
1563  *\r
1564  * GDI board drawing routines\r
1565  *\r
1566 \*---------------------------------------------------------------------------*/\r
1567 \r
1568 /* [AS] Draw square using background texture */\r
1569 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1570 {\r
1571     XFORM   x;\r
1572 \r
1573     if( mode == 0 ) {\r
1574         return; /* Should never happen! */\r
1575     }\r
1576 \r
1577     SetGraphicsMode( dst, GM_ADVANCED );\r
1578 \r
1579     switch( mode ) {\r
1580     case 1:\r
1581         /* Identity */\r
1582         break;\r
1583     case 2:\r
1584         /* X reflection */\r
1585         x.eM11 = -1.0;\r
1586         x.eM12 = 0;\r
1587         x.eM21 = 0;\r
1588         x.eM22 = 1.0;\r
1589         x.eDx = (FLOAT) dw + dx - 1;\r
1590         x.eDy = 0;\r
1591         dx = 0;\r
1592         SetWorldTransform( dst, &x );\r
1593         break;\r
1594     case 3:\r
1595         /* Y reflection */\r
1596         x.eM11 = 1.0;\r
1597         x.eM12 = 0;\r
1598         x.eM21 = 0;\r
1599         x.eM22 = -1.0;\r
1600         x.eDx = 0;\r
1601         x.eDy = (FLOAT) dh + dy - 1;\r
1602         dy = 0;\r
1603         SetWorldTransform( dst, &x );\r
1604         break;\r
1605     case 4:\r
1606         /* X/Y flip */\r
1607         x.eM11 = 0;\r
1608         x.eM12 = 1.0;\r
1609         x.eM21 = 1.0;\r
1610         x.eM22 = 0;\r
1611         x.eDx = (FLOAT) dx;\r
1612         x.eDy = (FLOAT) dy;\r
1613         dx = 0;\r
1614         dy = 0;\r
1615         SetWorldTransform( dst, &x );\r
1616         break;\r
1617     }\r
1618 \r
1619     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1620 \r
1621     x.eM11 = 1.0;\r
1622     x.eM12 = 0;\r
1623     x.eM21 = 0;\r
1624     x.eM22 = 1.0;\r
1625     x.eDx = 0;\r
1626     x.eDy = 0;\r
1627     SetWorldTransform( dst, &x );\r
1628 \r
1629     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1630 }\r
1631 \r
1632 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1633 enum {\r
1634     PM_WP = (int) WhitePawn, \r
1635     PM_WN = (int) WhiteKnight, \r
1636     PM_WB = (int) WhiteBishop, \r
1637     PM_WR = (int) WhiteRook, \r
1638     PM_WQ = (int) WhiteQueen, \r
1639     PM_WF = (int) WhiteFerz, \r
1640     PM_WW = (int) WhiteWazir, \r
1641     PM_WE = (int) WhiteAlfil, \r
1642     PM_WM = (int) WhiteMan, \r
1643     PM_WO = (int) WhiteCannon, \r
1644     PM_WU = (int) WhiteUnicorn, \r
1645     PM_WH = (int) WhiteNightrider, \r
1646     PM_WA = (int) WhiteAngel, \r
1647     PM_WC = (int) WhiteMarshall, \r
1648     PM_WAB = (int) WhiteCardinal, \r
1649     PM_WD = (int) WhiteDragon, \r
1650     PM_WL = (int) WhiteLance, \r
1651     PM_WS = (int) WhiteCobra, \r
1652     PM_WV = (int) WhiteFalcon, \r
1653     PM_WSG = (int) WhiteSilver, \r
1654     PM_WG = (int) WhiteGrasshopper, \r
1655     PM_WK = (int) WhiteKing,\r
1656     PM_BP = (int) BlackPawn, \r
1657     PM_BN = (int) BlackKnight, \r
1658     PM_BB = (int) BlackBishop, \r
1659     PM_BR = (int) BlackRook, \r
1660     PM_BQ = (int) BlackQueen, \r
1661     PM_BF = (int) BlackFerz, \r
1662     PM_BW = (int) BlackWazir, \r
1663     PM_BE = (int) BlackAlfil, \r
1664     PM_BM = (int) BlackMan,\r
1665     PM_BO = (int) BlackCannon, \r
1666     PM_BU = (int) BlackUnicorn, \r
1667     PM_BH = (int) BlackNightrider, \r
1668     PM_BA = (int) BlackAngel, \r
1669     PM_BC = (int) BlackMarshall, \r
1670     PM_BG = (int) BlackGrasshopper, \r
1671     PM_BAB = (int) BlackCardinal,\r
1672     PM_BD = (int) BlackDragon,\r
1673     PM_BL = (int) BlackLance,\r
1674     PM_BS = (int) BlackCobra,\r
1675     PM_BV = (int) BlackFalcon,\r
1676     PM_BSG = (int) BlackSilver,\r
1677     PM_BK = (int) BlackKing\r
1678 };\r
1679 \r
1680 static HFONT hPieceFont = NULL;\r
1681 static HBITMAP hPieceMask[(int) EmptySquare];\r
1682 static HBITMAP hPieceFace[(int) EmptySquare];\r
1683 static int fontBitmapSquareSize = 0;\r
1684 static char pieceToFontChar[(int) EmptySquare] =\r
1685                               { 'p', 'n', 'b', 'r', 'q', \r
1686                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1687                       'k', 'o', 'm', 'v', 't', 'w', \r
1688                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1689                                                               'l' };\r
1690 \r
1691 extern BOOL SetCharTable( char *table, const char * map );\r
1692 /* [HGM] moved to backend.c */\r
1693 \r
1694 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1695 {\r
1696     HBRUSH hbrush;\r
1697     BYTE r1 = GetRValue( color );\r
1698     BYTE g1 = GetGValue( color );\r
1699     BYTE b1 = GetBValue( color );\r
1700     BYTE r2 = r1 / 2;\r
1701     BYTE g2 = g1 / 2;\r
1702     BYTE b2 = b1 / 2;\r
1703     RECT rc;\r
1704 \r
1705     /* Create a uniform background first */\r
1706     hbrush = CreateSolidBrush( color );\r
1707     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1708     FillRect( hdc, &rc, hbrush );\r
1709     DeleteObject( hbrush );\r
1710     \r
1711     if( mode == 1 ) {\r
1712         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1713         int steps = squareSize / 2;\r
1714         int i;\r
1715 \r
1716         for( i=0; i<steps; i++ ) {\r
1717             BYTE r = r1 - (r1-r2) * i / steps;\r
1718             BYTE g = g1 - (g1-g2) * i / steps;\r
1719             BYTE b = b1 - (b1-b2) * i / steps;\r
1720 \r
1721             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1722             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1723             FillRect( hdc, &rc, hbrush );\r
1724             DeleteObject(hbrush);\r
1725         }\r
1726     }\r
1727     else if( mode == 2 ) {\r
1728         /* Diagonal gradient, good more or less for every piece */\r
1729         POINT triangle[3];\r
1730         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1731         HBRUSH hbrush_old;\r
1732         int steps = squareSize;\r
1733         int i;\r
1734 \r
1735         triangle[0].x = squareSize - steps;\r
1736         triangle[0].y = squareSize;\r
1737         triangle[1].x = squareSize;\r
1738         triangle[1].y = squareSize;\r
1739         triangle[2].x = squareSize;\r
1740         triangle[2].y = squareSize - steps;\r
1741 \r
1742         for( i=0; i<steps; i++ ) {\r
1743             BYTE r = r1 - (r1-r2) * i / steps;\r
1744             BYTE g = g1 - (g1-g2) * i / steps;\r
1745             BYTE b = b1 - (b1-b2) * i / steps;\r
1746 \r
1747             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1748             hbrush_old = SelectObject( hdc, hbrush );\r
1749             Polygon( hdc, triangle, 3 );\r
1750             SelectObject( hdc, hbrush_old );\r
1751             DeleteObject(hbrush);\r
1752             triangle[0].x++;\r
1753             triangle[2].y++;\r
1754         }\r
1755 \r
1756         SelectObject( hdc, hpen );\r
1757     }\r
1758 }\r
1759 \r
1760 /*\r
1761     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1762     seems to work ok. The main problem here is to find the "inside" of a chess\r
1763     piece: follow the steps as explained below.\r
1764 */\r
1765 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1766 {\r
1767     HBITMAP hbm;\r
1768     HBITMAP hbm_old;\r
1769     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1770     RECT rc;\r
1771     SIZE sz;\r
1772     POINT pt;\r
1773     int backColor = whitePieceColor; \r
1774     int foreColor = blackPieceColor;\r
1775     \r
1776     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1777         backColor = appData.fontBackColorWhite;\r
1778         foreColor = appData.fontForeColorWhite;\r
1779     }\r
1780     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1781         backColor = appData.fontBackColorBlack;\r
1782         foreColor = appData.fontForeColorBlack;\r
1783     }\r
1784 \r
1785     /* Mask */\r
1786     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1787 \r
1788     hbm_old = SelectObject( hdc, hbm );\r
1789 \r
1790     rc.left = 0;\r
1791     rc.top = 0;\r
1792     rc.right = squareSize;\r
1793     rc.bottom = squareSize;\r
1794 \r
1795     /* Step 1: background is now black */\r
1796     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1797 \r
1798     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1799 \r
1800     pt.x = (squareSize - sz.cx) / 2;\r
1801     pt.y = (squareSize - sz.cy) / 2;\r
1802 \r
1803     SetBkMode( hdc, TRANSPARENT );\r
1804     SetTextColor( hdc, chroma );\r
1805     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1806     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1807 \r
1808     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1809     /* Step 3: the area outside the piece is filled with white */\r
1810 //    FloodFill( hdc, 0, 0, chroma );\r
1811     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1812     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1813     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1814     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1815     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1816     /* \r
1817         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1818         but if the start point is not inside the piece we're lost!\r
1819         There should be a better way to do this... if we could create a region or path\r
1820         from the fill operation we would be fine for example.\r
1821     */\r
1822 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1823     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1824 \r
1825     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1826         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1827         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1828 \r
1829         SelectObject( dc2, bm2 );\r
1830         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1831         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1832         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1833         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1834         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1835 \r
1836         DeleteDC( dc2 );\r
1837         DeleteObject( bm2 );\r
1838     }\r
1839 \r
1840     SetTextColor( hdc, 0 );\r
1841     /* \r
1842         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1843         draw the piece again in black for safety.\r
1844     */\r
1845     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1846 \r
1847     SelectObject( hdc, hbm_old );\r
1848 \r
1849     if( hPieceMask[index] != NULL ) {\r
1850         DeleteObject( hPieceMask[index] );\r
1851     }\r
1852 \r
1853     hPieceMask[index] = hbm;\r
1854 \r
1855     /* Face */\r
1856     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1857 \r
1858     SelectObject( hdc, hbm );\r
1859 \r
1860     {\r
1861         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1862         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1863         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1864 \r
1865         SelectObject( dc1, hPieceMask[index] );\r
1866         SelectObject( dc2, bm2 );\r
1867         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1868         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1869         \r
1870         /* \r
1871             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1872             the piece background and deletes (makes transparent) the rest.\r
1873             Thanks to that mask, we are free to paint the background with the greates\r
1874             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1875             We use this, to make gradients and give the pieces a "roundish" look.\r
1876         */\r
1877         SetPieceBackground( hdc, backColor, 2 );\r
1878         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1879 \r
1880         DeleteDC( dc2 );\r
1881         DeleteDC( dc1 );\r
1882         DeleteObject( bm2 );\r
1883     }\r
1884 \r
1885     SetTextColor( hdc, foreColor );\r
1886     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1887 \r
1888     SelectObject( hdc, hbm_old );\r
1889 \r
1890     if( hPieceFace[index] != NULL ) {\r
1891         DeleteObject( hPieceFace[index] );\r
1892     }\r
1893 \r
1894     hPieceFace[index] = hbm;\r
1895 }\r
1896 \r
1897 static int TranslatePieceToFontPiece( int piece )\r
1898 {\r
1899     switch( piece ) {\r
1900     case BlackPawn:\r
1901         return PM_BP;\r
1902     case BlackKnight:\r
1903         return PM_BN;\r
1904     case BlackBishop:\r
1905         return PM_BB;\r
1906     case BlackRook:\r
1907         return PM_BR;\r
1908     case BlackQueen:\r
1909         return PM_BQ;\r
1910     case BlackKing:\r
1911         return PM_BK;\r
1912     case WhitePawn:\r
1913         return PM_WP;\r
1914     case WhiteKnight:\r
1915         return PM_WN;\r
1916     case WhiteBishop:\r
1917         return PM_WB;\r
1918     case WhiteRook:\r
1919         return PM_WR;\r
1920     case WhiteQueen:\r
1921         return PM_WQ;\r
1922     case WhiteKing:\r
1923         return PM_WK;\r
1924 \r
1925     case BlackAngel:\r
1926         return PM_BA;\r
1927     case BlackMarshall:\r
1928         return PM_BC;\r
1929     case BlackFerz:\r
1930         return PM_BF;\r
1931     case BlackNightrider:\r
1932         return PM_BH;\r
1933     case BlackAlfil:\r
1934         return PM_BE;\r
1935     case BlackWazir:\r
1936         return PM_BW;\r
1937     case BlackUnicorn:\r
1938         return PM_BU;\r
1939     case BlackCannon:\r
1940         return PM_BO;\r
1941     case BlackGrasshopper:\r
1942         return PM_BG;\r
1943     case BlackMan:\r
1944         return PM_BM;\r
1945     case BlackSilver:\r
1946         return PM_BSG;\r
1947     case BlackLance:\r
1948         return PM_BL;\r
1949     case BlackFalcon:\r
1950         return PM_BV;\r
1951     case BlackCobra:\r
1952         return PM_BS;\r
1953     case BlackCardinal:\r
1954         return PM_BAB;\r
1955     case BlackDragon:\r
1956         return PM_BD;\r
1957 \r
1958     case WhiteAngel:\r
1959         return PM_WA;\r
1960     case WhiteMarshall:\r
1961         return PM_WC;\r
1962     case WhiteFerz:\r
1963         return PM_WF;\r
1964     case WhiteNightrider:\r
1965         return PM_WH;\r
1966     case WhiteAlfil:\r
1967         return PM_WE;\r
1968     case WhiteWazir:\r
1969         return PM_WW;\r
1970     case WhiteUnicorn:\r
1971         return PM_WU;\r
1972     case WhiteCannon:\r
1973         return PM_WO;\r
1974     case WhiteGrasshopper:\r
1975         return PM_WG;\r
1976     case WhiteMan:\r
1977         return PM_WM;\r
1978     case WhiteSilver:\r
1979         return PM_WSG;\r
1980     case WhiteLance:\r
1981         return PM_WL;\r
1982     case WhiteFalcon:\r
1983         return PM_WV;\r
1984     case WhiteCobra:\r
1985         return PM_WS;\r
1986     case WhiteCardinal:\r
1987         return PM_WAB;\r
1988     case WhiteDragon:\r
1989         return PM_WD;\r
1990     }\r
1991 \r
1992     return 0;\r
1993 }\r
1994 \r
1995 void CreatePiecesFromFont()\r
1996 {\r
1997     LOGFONT lf;\r
1998     HDC hdc_window = NULL;\r
1999     HDC hdc = NULL;\r
2000     HFONT hfont_old;\r
2001     int fontHeight;\r
2002     int i;\r
2003 \r
2004     if( fontBitmapSquareSize < 0 ) {\r
2005         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2006         return;\r
2007     }\r
2008 \r
2009     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2010             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2011         fontBitmapSquareSize = -1;\r
2012         return;\r
2013     }\r
2014 \r
2015     if( fontBitmapSquareSize != squareSize ) {\r
2016         hdc_window = GetDC( hwndMain );\r
2017         hdc = CreateCompatibleDC( hdc_window );\r
2018 \r
2019         if( hPieceFont != NULL ) {\r
2020             DeleteObject( hPieceFont );\r
2021         }\r
2022         else {\r
2023             for( i=0; i<=(int)BlackKing; i++ ) {\r
2024                 hPieceMask[i] = NULL;\r
2025                 hPieceFace[i] = NULL;\r
2026             }\r
2027         }\r
2028 \r
2029         fontHeight = 75;\r
2030 \r
2031         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2032             fontHeight = appData.fontPieceSize;\r
2033         }\r
2034 \r
2035         fontHeight = (fontHeight * squareSize) / 100;\r
2036 \r
2037         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2038         lf.lfWidth = 0;\r
2039         lf.lfEscapement = 0;\r
2040         lf.lfOrientation = 0;\r
2041         lf.lfWeight = FW_NORMAL;\r
2042         lf.lfItalic = 0;\r
2043         lf.lfUnderline = 0;\r
2044         lf.lfStrikeOut = 0;\r
2045         lf.lfCharSet = DEFAULT_CHARSET;\r
2046         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2047         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2048         lf.lfQuality = PROOF_QUALITY;\r
2049         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2050         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2051         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2052 \r
2053         hPieceFont = CreateFontIndirect( &lf );\r
2054 \r
2055         if( hPieceFont == NULL ) {\r
2056             fontBitmapSquareSize = -2;\r
2057         }\r
2058         else {\r
2059             /* Setup font-to-piece character table */\r
2060             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2061                 /* No (or wrong) global settings, try to detect the font */\r
2062                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2063                     /* Alpha */\r
2064                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2065                 }\r
2066                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2067                     /* DiagramTT* family */\r
2068                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2069                 }\r
2070                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2071                     /* Fairy symbols */\r
2072                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2073                 }\r
2074                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2075                     /* Good Companion (Some characters get warped as literal :-( */\r
2076                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2077                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2078                     SetCharTable(pieceToFontChar, s);\r
2079                 }\r
2080                 else {\r
2081                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2082                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2083                 }\r
2084             }\r
2085 \r
2086             /* Create bitmaps */\r
2087             hfont_old = SelectObject( hdc, hPieceFont );\r
2088             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2089                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2090                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2091 \r
2092             SelectObject( hdc, hfont_old );\r
2093 \r
2094             fontBitmapSquareSize = squareSize;\r
2095         }\r
2096     }\r
2097 \r
2098     if( hdc != NULL ) {\r
2099         DeleteDC( hdc );\r
2100     }\r
2101 \r
2102     if( hdc_window != NULL ) {\r
2103         ReleaseDC( hwndMain, hdc_window );\r
2104     }\r
2105 }\r
2106 \r
2107 HBITMAP\r
2108 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2109 {\r
2110   char name[128];\r
2111 \r
2112     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2113   if (gameInfo.event &&\r
2114       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2115       strcmp(name, "k80s") == 0) {\r
2116     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2117   }\r
2118   return LoadBitmap(hinst, name);\r
2119 }\r
2120 \r
2121 \r
2122 /* Insert a color into the program's logical palette\r
2123    structure.  This code assumes the given color is\r
2124    the result of the RGB or PALETTERGB macro, and it\r
2125    knows how those macros work (which is documented).\r
2126 */\r
2127 VOID\r
2128 InsertInPalette(COLORREF color)\r
2129 {\r
2130   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2131 \r
2132   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2133     DisplayFatalError(_("Too many colors"), 0, 1);\r
2134     pLogPal->palNumEntries--;\r
2135     return;\r
2136   }\r
2137 \r
2138   pe->peFlags = (char) 0;\r
2139   pe->peRed = (char) (0xFF & color);\r
2140   pe->peGreen = (char) (0xFF & (color >> 8));\r
2141   pe->peBlue = (char) (0xFF & (color >> 16));\r
2142   return;\r
2143 }\r
2144 \r
2145 \r
2146 VOID\r
2147 InitDrawingColors()\r
2148 {\r
2149   if (pLogPal == NULL) {\r
2150     /* Allocate enough memory for a logical palette with\r
2151      * PALETTESIZE entries and set the size and version fields\r
2152      * of the logical palette structure.\r
2153      */\r
2154     pLogPal = (NPLOGPALETTE)\r
2155       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2156                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2157     pLogPal->palVersion    = 0x300;\r
2158   }\r
2159   pLogPal->palNumEntries = 0;\r
2160 \r
2161   InsertInPalette(lightSquareColor);\r
2162   InsertInPalette(darkSquareColor);\r
2163   InsertInPalette(whitePieceColor);\r
2164   InsertInPalette(blackPieceColor);\r
2165   InsertInPalette(highlightSquareColor);\r
2166   InsertInPalette(premoveHighlightColor);\r
2167 \r
2168   /*  create a logical color palette according the information\r
2169    *  in the LOGPALETTE structure.\r
2170    */\r
2171   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2172 \r
2173   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2174   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2175   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2176   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2177   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2178   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2179   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2180   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2181   /* [AS] Force rendering of the font-based pieces */\r
2182   if( fontBitmapSquareSize > 0 ) {\r
2183     fontBitmapSquareSize = 0;\r
2184   }\r
2185 }\r
2186 \r
2187 \r
2188 int\r
2189 BoardWidth(int boardSize, int n)\r
2190 { /* [HGM] argument n added to allow different width and height */\r
2191   int lineGap = sizeInfo[boardSize].lineGap;\r
2192 \r
2193   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2194       lineGap = appData.overrideLineGap;\r
2195   }\r
2196 \r
2197   return (n + 1) * lineGap +\r
2198           n * sizeInfo[boardSize].squareSize;\r
2199 }\r
2200 \r
2201 /* Respond to board resize by dragging edge */\r
2202 VOID\r
2203 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2204 {\r
2205   BoardSize newSize = NUM_SIZES - 1;\r
2206   static int recurse = 0;\r
2207   if (IsIconic(hwndMain)) return;\r
2208   if (recurse > 0) return;\r
2209   recurse++;\r
2210   while (newSize > 0) {\r
2211         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2212         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2213            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2214     newSize--;\r
2215   } \r
2216   boardSize = newSize;\r
2217   InitDrawingSizes(boardSize, flags);\r
2218   recurse--;\r
2219 }\r
2220 \r
2221 \r
2222 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2223 \r
2224 VOID\r
2225 InitDrawingSizes(BoardSize boardSize, int flags)\r
2226 {\r
2227   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2228   ChessSquare piece;\r
2229   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2230   HDC hdc;\r
2231   SIZE clockSize, messageSize;\r
2232   HFONT oldFont;\r
2233   char buf[MSG_SIZ];\r
2234   char *str;\r
2235   HMENU hmenu = GetMenu(hwndMain);\r
2236   RECT crect, wrect, oldRect;\r
2237   int offby;\r
2238   LOGBRUSH logbrush;\r
2239   VariantClass v = gameInfo.variant;\r
2240 \r
2241   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2242   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2243 \r
2244   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2245   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2246   oldBoardSize = boardSize;\r
2247 \r
2248   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2249   { // correct board size to one where built-in pieces exist\r
2250     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2251        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2252       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2253       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2254       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2255       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2256       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2257                                    boardSize = SizeMiddling;\r
2258     }\r
2259   }\r
2260   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2261 \r
2262   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2263   oldRect.top = wpMain.y;\r
2264   oldRect.right = wpMain.x + wpMain.width;\r
2265   oldRect.bottom = wpMain.y + wpMain.height;\r
2266 \r
2267   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2268   smallLayout = sizeInfo[boardSize].smallLayout;\r
2269   squareSize = sizeInfo[boardSize].squareSize;\r
2270   lineGap = sizeInfo[boardSize].lineGap;\r
2271   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2272 \r
2273   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2274       lineGap = appData.overrideLineGap;\r
2275   }\r
2276 \r
2277   if (tinyLayout != oldTinyLayout) {\r
2278     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2279     if (tinyLayout) {\r
2280       style &= ~WS_SYSMENU;\r
2281       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2282                  "&Minimize\tCtrl+F4");\r
2283     } else {\r
2284       style |= WS_SYSMENU;\r
2285       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2286     }\r
2287     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2288 \r
2289     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2290       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2291         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2292     }\r
2293     DrawMenuBar(hwndMain);\r
2294   }\r
2295 \r
2296   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2297   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2298 \r
2299   /* Get text area sizes */\r
2300   hdc = GetDC(hwndMain);\r
2301   if (appData.clockMode) {\r
2302     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2303   } else {\r
2304     snprintf(buf, MSG_SIZ, _("White"));\r
2305   }\r
2306   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2307   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2308   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2309   str = _("We only care about the height here");\r
2310   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2311   SelectObject(hdc, oldFont);\r
2312   ReleaseDC(hwndMain, hdc);\r
2313 \r
2314   /* Compute where everything goes */\r
2315   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2316         /* [HGM] logo: if either logo is on, reserve space for it */\r
2317         logoHeight =  2*clockSize.cy;\r
2318         leftLogoRect.left   = OUTER_MARGIN;\r
2319         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2320         leftLogoRect.top    = OUTER_MARGIN;\r
2321         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2322 \r
2323         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2324         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2325         rightLogoRect.top    = OUTER_MARGIN;\r
2326         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2327 \r
2328 \r
2329     whiteRect.left = leftLogoRect.right;\r
2330     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2331     whiteRect.top = OUTER_MARGIN;\r
2332     whiteRect.bottom = whiteRect.top + logoHeight;\r
2333 \r
2334     blackRect.right = rightLogoRect.left;\r
2335     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2336     blackRect.top = whiteRect.top;\r
2337     blackRect.bottom = whiteRect.bottom;\r
2338   } else {\r
2339     whiteRect.left = OUTER_MARGIN;\r
2340     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2341     whiteRect.top = OUTER_MARGIN;\r
2342     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2343 \r
2344     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2345     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2346     blackRect.top = whiteRect.top;\r
2347     blackRect.bottom = whiteRect.bottom;\r
2348 \r
2349     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2350   }\r
2351 \r
2352   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2353   if (appData.showButtonBar) {\r
2354     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2355       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2356   } else {\r
2357     messageRect.right = OUTER_MARGIN + boardWidth;\r
2358   }\r
2359   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2360   messageRect.bottom = messageRect.top + messageSize.cy;\r
2361 \r
2362   boardRect.left = OUTER_MARGIN;\r
2363   boardRect.right = boardRect.left + boardWidth;\r
2364   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2365   boardRect.bottom = boardRect.top + boardHeight;\r
2366 \r
2367   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2368   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2369   oldTinyLayout = tinyLayout;\r
2370   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2371   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2372     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2373   winW *= 1 + twoBoards;\r
2374   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2375   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2376   wpMain.height = winH; //       without disturbing window attachments\r
2377   GetWindowRect(hwndMain, &wrect);\r
2378   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2379                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2380 \r
2381   // [HGM] placement: let attached windows follow size change.\r
2382   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2383   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2384   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2385   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2386   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2387 \r
2388   /* compensate if menu bar wrapped */\r
2389   GetClientRect(hwndMain, &crect);\r
2390   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2391   wpMain.height += offby;\r
2392   switch (flags) {\r
2393   case WMSZ_TOPLEFT:\r
2394     SetWindowPos(hwndMain, NULL, \r
2395                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2396                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2397     break;\r
2398 \r
2399   case WMSZ_TOPRIGHT:\r
2400   case WMSZ_TOP:\r
2401     SetWindowPos(hwndMain, NULL, \r
2402                  wrect.left, wrect.bottom - wpMain.height, \r
2403                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2404     break;\r
2405 \r
2406   case WMSZ_BOTTOMLEFT:\r
2407   case WMSZ_LEFT:\r
2408     SetWindowPos(hwndMain, NULL, \r
2409                  wrect.right - wpMain.width, wrect.top, \r
2410                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2411     break;\r
2412 \r
2413   case WMSZ_BOTTOMRIGHT:\r
2414   case WMSZ_BOTTOM:\r
2415   case WMSZ_RIGHT:\r
2416   default:\r
2417     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2418                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2419     break;\r
2420   }\r
2421 \r
2422   hwndPause = NULL;\r
2423   for (i = 0; i < N_BUTTONS; i++) {\r
2424     if (buttonDesc[i].hwnd != NULL) {\r
2425       DestroyWindow(buttonDesc[i].hwnd);\r
2426       buttonDesc[i].hwnd = NULL;\r
2427     }\r
2428     if (appData.showButtonBar) {\r
2429       buttonDesc[i].hwnd =\r
2430         CreateWindow("BUTTON", buttonDesc[i].label,\r
2431                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2432                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2433                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2434                      (HMENU) buttonDesc[i].id,\r
2435                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2436       if (tinyLayout) {\r
2437         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2438                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2439                     MAKELPARAM(FALSE, 0));\r
2440       }\r
2441       if (buttonDesc[i].id == IDM_Pause)\r
2442         hwndPause = buttonDesc[i].hwnd;\r
2443       buttonDesc[i].wndproc = (WNDPROC)\r
2444         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2445     }\r
2446   }\r
2447   if (gridPen != NULL) DeleteObject(gridPen);\r
2448   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2449   if (premovePen != NULL) DeleteObject(premovePen);\r
2450   if (lineGap != 0) {\r
2451     logbrush.lbStyle = BS_SOLID;\r
2452     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2453     gridPen =\r
2454       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2455                    lineGap, &logbrush, 0, NULL);\r
2456     logbrush.lbColor = highlightSquareColor;\r
2457     highlightPen =\r
2458       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2459                    lineGap, &logbrush, 0, NULL);\r
2460 \r
2461     logbrush.lbColor = premoveHighlightColor; \r
2462     premovePen =\r
2463       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2464                    lineGap, &logbrush, 0, NULL);\r
2465 \r
2466     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2467     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2468       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2469       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2470         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2471       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2472         BOARD_WIDTH * (squareSize + lineGap);\r
2473       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2474     }\r
2475     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2476       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2477       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2478         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2479         lineGap / 2 + (i * (squareSize + lineGap));\r
2480       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2481         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2482       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2483     }\r
2484   }\r
2485 \r
2486   /* [HGM] Licensing requirement */\r
2487 #ifdef GOTHIC\r
2488   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2489 #endif\r
2490 #ifdef FALCON\r
2491   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2492 #endif\r
2493   GothicPopUp( "", VariantNormal);\r
2494 \r
2495 \r
2496 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2497 \r
2498   /* Load piece bitmaps for this board size */\r
2499   for (i=0; i<=2; i++) {\r
2500     for (piece = WhitePawn;\r
2501          (int) piece < (int) BlackPawn;\r
2502          piece = (ChessSquare) ((int) piece + 1)) {\r
2503       if (pieceBitmap[i][piece] != NULL)\r
2504         DeleteObject(pieceBitmap[i][piece]);\r
2505     }\r
2506   }\r
2507 \r
2508   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2509   // Orthodox Chess pieces\r
2510   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2511   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2512   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2513   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2514   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2515   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2516   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2517   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2518   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2519   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2520   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2521   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2522   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2523   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2524   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2525   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2526     // in Shogi, Hijack the unused Queen for Lance\r
2527     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2528     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2529     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2530   } else {\r
2531     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2532     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2533     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2534   }\r
2535 \r
2536   if(squareSize <= 72 && squareSize >= 33) { \r
2537     /* A & C are available in most sizes now */\r
2538     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2539       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2540       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2541       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2542       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2543       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2544       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2545       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2546       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2547       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2548       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2549       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2550       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2551     } else { // Smirf-like\r
2552       if(gameInfo.variant == VariantSChess) {\r
2553         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2554         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2555         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2556       } else {\r
2557         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2558         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2559         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2560       }\r
2561     }\r
2562     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2563       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2564       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2565       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2566     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2567       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2568       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2569       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2570     } else { // WinBoard standard\r
2571       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2572       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2573       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2574     }\r
2575   }\r
2576 \r
2577 \r
2578   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2579     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2580     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2581     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2582     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2583     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2584     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2585     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2586     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2587     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2588     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2589     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2590     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2591     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2592     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2593     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2594     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2595     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2596     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2597     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2598     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2599     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2600     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2601     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2602     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2603     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2604     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2605     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2606     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2607     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2608     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2609 \r
2610     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2611       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2612       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2613       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2614       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2615       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2616       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2617       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2618       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2619       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2620       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2621       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2622       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2623     } else {\r
2624       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2625       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2626       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2627       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2628       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2629       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2630       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2631       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2632       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2633       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2634       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2635       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2636     }\r
2637 \r
2638   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2639     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2640     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2641     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2642     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2643     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2644     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2645     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2646     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2647     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2648     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2649     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2650     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2651     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2652     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2653   }\r
2654 \r
2655 \r
2656   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2657   /* special Shogi support in this size */\r
2658   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2659       for (piece = WhitePawn;\r
2660            (int) piece < (int) BlackPawn;\r
2661            piece = (ChessSquare) ((int) piece + 1)) {\r
2662         if (pieceBitmap[i][piece] != NULL)\r
2663           DeleteObject(pieceBitmap[i][piece]);\r
2664       }\r
2665     }\r
2666   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2667   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2668   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2669   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2670   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2671   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2672   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2673   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2674   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2675   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2676   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2677   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2678   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2679   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2680   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2681   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2682   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2683   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2684   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2685   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2686   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2687   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2688   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2689   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2690   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2691   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2692   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2693   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2694   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2695   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2696   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2697   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2698   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2699   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2700   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2701   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2702   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2703   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2704   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2705   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2706   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2707   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2708   minorSize = 0;\r
2709   }\r
2710 }\r
2711 \r
2712 HBITMAP\r
2713 PieceBitmap(ChessSquare p, int kind)\r
2714 {\r
2715   if ((int) p >= (int) BlackPawn)\r
2716     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2717 \r
2718   return pieceBitmap[kind][(int) p];\r
2719 }\r
2720 \r
2721 /***************************************************************/\r
2722 \r
2723 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2724 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2725 /*\r
2726 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2727 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2728 */\r
2729 \r
2730 VOID\r
2731 SquareToPos(int row, int column, int * x, int * y)\r
2732 {\r
2733   if (flipView) {\r
2734     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2735     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2736   } else {\r
2737     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2738     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2739   }\r
2740 }\r
2741 \r
2742 VOID\r
2743 DrawCoordsOnDC(HDC hdc)\r
2744 {\r
2745   static char files[] = "0123456789012345678901221098765432109876543210";\r
2746   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2747   char str[2] = { NULLCHAR, NULLCHAR };\r
2748   int oldMode, oldAlign, x, y, start, i;\r
2749   HFONT oldFont;\r
2750   HBRUSH oldBrush;\r
2751 \r
2752   if (!appData.showCoords)\r
2753     return;\r
2754 \r
2755   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2756 \r
2757   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2758   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2759   oldAlign = GetTextAlign(hdc);\r
2760   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2761 \r
2762   y = boardRect.top + lineGap;\r
2763   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2764 \r
2765   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2766   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2767     str[0] = files[start + i];\r
2768     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2769     y += squareSize + lineGap;\r
2770   }\r
2771 \r
2772   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2773 \r
2774   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2775   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2776     str[0] = ranks[start + i];\r
2777     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2778     x += squareSize + lineGap;\r
2779   }    \r
2780 \r
2781   SelectObject(hdc, oldBrush);\r
2782   SetBkMode(hdc, oldMode);\r
2783   SetTextAlign(hdc, oldAlign);\r
2784   SelectObject(hdc, oldFont);\r
2785 }\r
2786 \r
2787 VOID\r
2788 DrawGridOnDC(HDC hdc)\r
2789 {\r
2790   HPEN oldPen;\r
2791  \r
2792   if (lineGap != 0) {\r
2793     oldPen = SelectObject(hdc, gridPen);\r
2794     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2795     SelectObject(hdc, oldPen);\r
2796   }\r
2797 }\r
2798 \r
2799 #define HIGHLIGHT_PEN 0\r
2800 #define PREMOVE_PEN   1\r
2801 \r
2802 VOID\r
2803 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2804 {\r
2805   int x1, y1;\r
2806   HPEN oldPen, hPen;\r
2807   if (lineGap == 0) return;\r
2808   if (flipView) {\r
2809     x1 = boardRect.left +\r
2810       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2811     y1 = boardRect.top +\r
2812       lineGap/2 + y * (squareSize + lineGap);\r
2813   } else {\r
2814     x1 = boardRect.left +\r
2815       lineGap/2 + x * (squareSize + lineGap);\r
2816     y1 = boardRect.top +\r
2817       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2818   }\r
2819   hPen = pen ? premovePen : highlightPen;\r
2820   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2821   MoveToEx(hdc, x1, y1, NULL);\r
2822   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2823   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2824   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2825   LineTo(hdc, x1, y1);\r
2826   SelectObject(hdc, oldPen);\r
2827 }\r
2828 \r
2829 VOID\r
2830 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2831 {\r
2832   int i;\r
2833   for (i=0; i<2; i++) {\r
2834     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2835       DrawHighlightOnDC(hdc, TRUE,\r
2836                         h->sq[i].x, h->sq[i].y,\r
2837                         pen);\r
2838   }\r
2839 }\r
2840 \r
2841 /* Note: sqcolor is used only in monoMode */\r
2842 /* Note that this code is largely duplicated in woptions.c,\r
2843    function DrawSampleSquare, so that needs to be updated too */\r
2844 VOID\r
2845 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2846 {\r
2847   HBITMAP oldBitmap;\r
2848   HBRUSH oldBrush;\r
2849   int tmpSize;\r
2850 \r
2851   if (appData.blindfold) return;\r
2852 \r
2853   /* [AS] Use font-based pieces if needed */\r
2854   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2855     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2856     CreatePiecesFromFont();\r
2857 \r
2858     if( fontBitmapSquareSize == squareSize ) {\r
2859         int index = TranslatePieceToFontPiece(piece);\r
2860 \r
2861         SelectObject( tmphdc, hPieceMask[ index ] );\r
2862 \r
2863       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2864         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2865       else\r
2866         BitBlt( hdc,\r
2867             x, y,\r
2868             squareSize, squareSize,\r
2869             tmphdc,\r
2870             0, 0,\r
2871             SRCAND );\r
2872 \r
2873         SelectObject( tmphdc, hPieceFace[ index ] );\r
2874 \r
2875       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2876         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2877       else\r
2878         BitBlt( hdc,\r
2879             x, y,\r
2880             squareSize, squareSize,\r
2881             tmphdc,\r
2882             0, 0,\r
2883             SRCPAINT );\r
2884 \r
2885         return;\r
2886     }\r
2887   }\r
2888 \r
2889   if (appData.monoMode) {\r
2890     SelectObject(tmphdc, PieceBitmap(piece, \r
2891       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2892     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2893            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2894   } else {\r
2895     tmpSize = squareSize;\r
2896     if(minorSize &&\r
2897         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2898          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2899       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2900       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2901       x += (squareSize - minorSize)>>1;\r
2902       y += squareSize - minorSize - 2;\r
2903       tmpSize = minorSize;\r
2904     }\r
2905     if (color || appData.allWhite ) {\r
2906       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2907       if( color )\r
2908               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2909       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2910       if(appData.upsideDown && color==flipView)\r
2911         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2912       else\r
2913         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2914       /* Use black for outline of white pieces */\r
2915       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2916       if(appData.upsideDown && color==flipView)\r
2917         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2918       else\r
2919         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2920     } else {\r
2921       /* Use square color for details of black pieces */\r
2922       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2923       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2924       if(appData.upsideDown && !flipView)\r
2925         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2926       else\r
2927         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2928     }\r
2929     SelectObject(hdc, oldBrush);\r
2930     SelectObject(tmphdc, oldBitmap);\r
2931   }\r
2932 }\r
2933 \r
2934 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2935 int GetBackTextureMode( int algo )\r
2936 {\r
2937     int result = BACK_TEXTURE_MODE_DISABLED;\r
2938 \r
2939     switch( algo ) \r
2940     {\r
2941         case BACK_TEXTURE_MODE_PLAIN:\r
2942             result = 1; /* Always use identity map */\r
2943             break;\r
2944         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2945             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2946             break;\r
2947     }\r
2948 \r
2949     return result;\r
2950 }\r
2951 \r
2952 /* \r
2953     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2954     to handle redraws cleanly (as random numbers would always be different).\r
2955 */\r
2956 VOID RebuildTextureSquareInfo()\r
2957 {\r
2958     BITMAP bi;\r
2959     int lite_w = 0;\r
2960     int lite_h = 0;\r
2961     int dark_w = 0;\r
2962     int dark_h = 0;\r
2963     int row;\r
2964     int col;\r
2965 \r
2966     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2967 \r
2968     if( liteBackTexture != NULL ) {\r
2969         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2970             lite_w = bi.bmWidth;\r
2971             lite_h = bi.bmHeight;\r
2972         }\r
2973     }\r
2974 \r
2975     if( darkBackTexture != NULL ) {\r
2976         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2977             dark_w = bi.bmWidth;\r
2978             dark_h = bi.bmHeight;\r
2979         }\r
2980     }\r
2981 \r
2982     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2983         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2984             if( (col + row) & 1 ) {\r
2985                 /* Lite square */\r
2986                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2987                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2988                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2989                   else\r
2990                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2991                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2992                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2993                   else\r
2994                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2995                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2996                 }\r
2997             }\r
2998             else {\r
2999                 /* Dark square */\r
3000                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3001                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3002                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3003                   else\r
3004                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3005                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3006                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3007                   else\r
3008                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3009                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3010                 }\r
3011             }\r
3012         }\r
3013     }\r
3014 }\r
3015 \r
3016 /* [AS] Arrow highlighting support */\r
3017 \r
3018 static double A_WIDTH = 5; /* Width of arrow body */\r
3019 \r
3020 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3021 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3022 \r
3023 static double Sqr( double x )\r
3024 {\r
3025     return x*x;\r
3026 }\r
3027 \r
3028 static int Round( double x )\r
3029 {\r
3030     return (int) (x + 0.5);\r
3031 }\r
3032 \r
3033 /* Draw an arrow between two points using current settings */\r
3034 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3035 {\r
3036     POINT arrow[7];\r
3037     double dx, dy, j, k, x, y;\r
3038 \r
3039     if( d_x == s_x ) {\r
3040         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3041 \r
3042         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3043         arrow[0].y = s_y;\r
3044 \r
3045         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3046         arrow[1].y = d_y - h;\r
3047 \r
3048         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3049         arrow[2].y = d_y - h;\r
3050 \r
3051         arrow[3].x = d_x;\r
3052         arrow[3].y = d_y;\r
3053 \r
3054         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3055         arrow[5].y = d_y - h;\r
3056 \r
3057         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3058         arrow[4].y = d_y - h;\r
3059 \r
3060         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3061         arrow[6].y = s_y;\r
3062     }\r
3063     else if( d_y == s_y ) {\r
3064         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3065 \r
3066         arrow[0].x = s_x;\r
3067         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3068 \r
3069         arrow[1].x = d_x - w;\r
3070         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3071 \r
3072         arrow[2].x = d_x - w;\r
3073         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3074 \r
3075         arrow[3].x = d_x;\r
3076         arrow[3].y = d_y;\r
3077 \r
3078         arrow[5].x = d_x - w;\r
3079         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3080 \r
3081         arrow[4].x = d_x - w;\r
3082         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3083 \r
3084         arrow[6].x = s_x;\r
3085         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3086     }\r
3087     else {\r
3088         /* [AS] Needed a lot of paper for this! :-) */\r
3089         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3090         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3091   \r
3092         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3093 \r
3094         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3095 \r
3096         x = s_x;\r
3097         y = s_y;\r
3098 \r
3099         arrow[0].x = Round(x - j);\r
3100         arrow[0].y = Round(y + j*dx);\r
3101 \r
3102         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3103         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3104 \r
3105         if( d_x > s_x ) {\r
3106             x = (double) d_x - k;\r
3107             y = (double) d_y - k*dy;\r
3108         }\r
3109         else {\r
3110             x = (double) d_x + k;\r
3111             y = (double) d_y + k*dy;\r
3112         }\r
3113 \r
3114         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3115 \r
3116         arrow[6].x = Round(x - j);\r
3117         arrow[6].y = Round(y + j*dx);\r
3118 \r
3119         arrow[2].x = Round(arrow[6].x + 2*j);\r
3120         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3121 \r
3122         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3123         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3124 \r
3125         arrow[4].x = d_x;\r
3126         arrow[4].y = d_y;\r
3127 \r
3128         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3129         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3130     }\r
3131 \r
3132     Polygon( hdc, arrow, 7 );\r
3133 }\r
3134 \r
3135 /* [AS] Draw an arrow between two squares */\r
3136 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3137 {\r
3138     int s_x, s_y, d_x, d_y;\r
3139     HPEN hpen;\r
3140     HPEN holdpen;\r
3141     HBRUSH hbrush;\r
3142     HBRUSH holdbrush;\r
3143     LOGBRUSH stLB;\r
3144 \r
3145     if( s_col == d_col && s_row == d_row ) {\r
3146         return;\r
3147     }\r
3148 \r
3149     /* Get source and destination points */\r
3150     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3151     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3152 \r
3153     if( d_y > s_y ) {\r
3154         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3155     }\r
3156     else if( d_y < s_y ) {\r
3157         d_y += squareSize / 2 + squareSize / 4;\r
3158     }\r
3159     else {\r
3160         d_y += squareSize / 2;\r
3161     }\r
3162 \r
3163     if( d_x > s_x ) {\r
3164         d_x += squareSize / 2 - squareSize / 4;\r
3165     }\r
3166     else if( d_x < s_x ) {\r
3167         d_x += squareSize / 2 + squareSize / 4;\r
3168     }\r
3169     else {\r
3170         d_x += squareSize / 2;\r
3171     }\r
3172 \r
3173     s_x += squareSize / 2;\r
3174     s_y += squareSize / 2;\r
3175 \r
3176     /* Adjust width */\r
3177     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3178 \r
3179     /* Draw */\r
3180     stLB.lbStyle = BS_SOLID;\r
3181     stLB.lbColor = appData.highlightArrowColor;\r
3182     stLB.lbHatch = 0;\r
3183 \r
3184     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3185     holdpen = SelectObject( hdc, hpen );\r
3186     hbrush = CreateBrushIndirect( &stLB );\r
3187     holdbrush = SelectObject( hdc, hbrush );\r
3188 \r
3189     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3190 \r
3191     SelectObject( hdc, holdpen );\r
3192     SelectObject( hdc, holdbrush );\r
3193     DeleteObject( hpen );\r
3194     DeleteObject( hbrush );\r
3195 }\r
3196 \r
3197 BOOL HasHighlightInfo()\r
3198 {\r
3199     BOOL result = FALSE;\r
3200 \r
3201     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3202         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3203     {\r
3204         result = TRUE;\r
3205     }\r
3206 \r
3207     return result;\r
3208 }\r
3209 \r
3210 BOOL IsDrawArrowEnabled()\r
3211 {\r
3212     BOOL result = FALSE;\r
3213 \r
3214     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3215         result = TRUE;\r
3216     }\r
3217 \r
3218     return result;\r
3219 }\r
3220 \r
3221 VOID DrawArrowHighlight( HDC hdc )\r
3222 {\r
3223     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3224         DrawArrowBetweenSquares( hdc,\r
3225             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3226             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3227     }\r
3228 }\r
3229 \r
3230 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3231 {\r
3232     HRGN result = NULL;\r
3233 \r
3234     if( HasHighlightInfo() ) {\r
3235         int x1, y1, x2, y2;\r
3236         int sx, sy, dx, dy;\r
3237 \r
3238         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3239         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3240 \r
3241         sx = MIN( x1, x2 );\r
3242         sy = MIN( y1, y2 );\r
3243         dx = MAX( x1, x2 ) + squareSize;\r
3244         dy = MAX( y1, y2 ) + squareSize;\r
3245 \r
3246         result = CreateRectRgn( sx, sy, dx, dy );\r
3247     }\r
3248 \r
3249     return result;\r
3250 }\r
3251 \r
3252 /*\r
3253     Warning: this function modifies the behavior of several other functions. \r
3254     \r
3255     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3256     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3257     repaint is scattered all over the place, which is not good for features such as\r
3258     "arrow highlighting" that require a full repaint of the board.\r
3259 \r
3260     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3261     user interaction, when speed is not so important) but especially to avoid errors\r
3262     in the displayed graphics.\r
3263 \r
3264     In such patched places, I always try refer to this function so there is a single\r
3265     place to maintain knowledge.\r
3266     \r
3267     To restore the original behavior, just return FALSE unconditionally.\r
3268 */\r
3269 BOOL IsFullRepaintPreferrable()\r
3270 {\r
3271     BOOL result = FALSE;\r
3272 \r
3273     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3274         /* Arrow may appear on the board */\r
3275         result = TRUE;\r
3276     }\r
3277 \r
3278     return result;\r
3279 }\r
3280 \r
3281 /* \r
3282     This function is called by DrawPosition to know whether a full repaint must\r
3283     be forced or not.\r
3284 \r
3285     Only DrawPosition may directly call this function, which makes use of \r
3286     some state information. Other function should call DrawPosition specifying \r
3287     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3288 */\r
3289 BOOL DrawPositionNeedsFullRepaint()\r
3290 {\r
3291     BOOL result = FALSE;\r
3292 \r
3293     /* \r
3294         Probably a slightly better policy would be to trigger a full repaint\r
3295         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3296         but animation is fast enough that it's difficult to notice.\r
3297     */\r
3298     if( animInfo.piece == EmptySquare ) {\r
3299         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3300             result = TRUE;\r
3301         }\r
3302     }\r
3303 \r
3304     return result;\r
3305 }\r
3306 \r
3307 VOID\r
3308 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3309 {\r
3310   int row, column, x, y, square_color, piece_color;\r
3311   ChessSquare piece;\r
3312   HBRUSH oldBrush;\r
3313   HDC texture_hdc = NULL;\r
3314 \r
3315   /* [AS] Initialize background textures if needed */\r
3316   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3317       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3318       if( backTextureSquareSize != squareSize \r
3319        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3320           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3321           backTextureSquareSize = squareSize;\r
3322           RebuildTextureSquareInfo();\r
3323       }\r
3324 \r
3325       texture_hdc = CreateCompatibleDC( hdc );\r
3326   }\r
3327 \r
3328   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3329     for (column = 0; column < BOARD_WIDTH; column++) {\r
3330   \r
3331       SquareToPos(row, column, &x, &y);\r
3332 \r
3333       piece = board[row][column];\r
3334 \r
3335       square_color = ((column + row) % 2) == 1;\r
3336       if( gameInfo.variant == VariantXiangqi ) {\r
3337           square_color = !InPalace(row, column);\r
3338           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3339           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3340       }\r
3341       piece_color = (int) piece < (int) BlackPawn;\r
3342 \r
3343 \r
3344       /* [HGM] holdings file: light square or black */\r
3345       if(column == BOARD_LEFT-2) {\r
3346             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3347                 square_color = 1;\r
3348             else {\r
3349                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3350                 continue;\r
3351             }\r
3352       } else\r
3353       if(column == BOARD_RGHT + 1 ) {\r
3354             if( row < gameInfo.holdingsSize )\r
3355                 square_color = 1;\r
3356             else {\r
3357                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3358                 continue;\r
3359             }\r
3360       }\r
3361       if(column == BOARD_LEFT-1 ) /* left align */\r
3362             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3363       else if( column == BOARD_RGHT) /* right align */\r
3364             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3365       else\r
3366       if (appData.monoMode) {\r
3367         if (piece == EmptySquare) {\r
3368           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3369                  square_color ? WHITENESS : BLACKNESS);\r
3370         } else {\r
3371           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3372         }\r
3373       } \r
3374       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3375           /* [AS] Draw the square using a texture bitmap */\r
3376           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3377           int r = row, c = column; // [HGM] do not flip board in flipView\r
3378           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3379 \r
3380           DrawTile( x, y, \r
3381               squareSize, squareSize, \r
3382               hdc, \r
3383               texture_hdc,\r
3384               backTextureSquareInfo[r][c].mode,\r
3385               backTextureSquareInfo[r][c].x,\r
3386               backTextureSquareInfo[r][c].y );\r
3387 \r
3388           SelectObject( texture_hdc, hbm );\r
3389 \r
3390           if (piece != EmptySquare) {\r
3391               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3392           }\r
3393       }\r
3394       else {\r
3395         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3396 \r
3397         oldBrush = SelectObject(hdc, brush );\r
3398         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3399         SelectObject(hdc, oldBrush);\r
3400         if (piece != EmptySquare)\r
3401           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3402       }\r
3403     }\r
3404   }\r
3405 \r
3406   if( texture_hdc != NULL ) {\r
3407     DeleteDC( texture_hdc );\r
3408   }\r
3409 }\r
3410 \r
3411 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3412 void fputDW(FILE *f, int x)\r
3413 {\r
3414         fputc(x     & 255, f);\r
3415         fputc(x>>8  & 255, f);\r
3416         fputc(x>>16 & 255, f);\r
3417         fputc(x>>24 & 255, f);\r
3418 }\r
3419 \r
3420 #define MAX_CLIPS 200   /* more than enough */\r
3421 \r
3422 VOID\r
3423 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3424 {\r
3425 //  HBITMAP bufferBitmap;\r
3426   BITMAP bi;\r
3427 //  RECT Rect;\r
3428   HDC tmphdc;\r
3429   HBITMAP hbm;\r
3430   int w = 100, h = 50;\r
3431 \r
3432   if(logo == NULL) {\r
3433     if(!logoHeight) return;\r
3434     FillRect( hdc, &logoRect, whitePieceBrush );\r
3435   }\r
3436 //  GetClientRect(hwndMain, &Rect);\r
3437 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3438 //                                      Rect.bottom-Rect.top+1);\r
3439   tmphdc = CreateCompatibleDC(hdc);\r
3440   hbm = SelectObject(tmphdc, logo);\r
3441   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3442             w = bi.bmWidth;\r
3443             h = bi.bmHeight;\r
3444   }\r
3445   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3446                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3447   SelectObject(tmphdc, hbm);\r
3448   DeleteDC(tmphdc);\r
3449 }\r
3450 \r
3451 VOID\r
3452 DisplayLogos()\r
3453 {\r
3454   if(logoHeight) {\r
3455         HDC hdc = GetDC(hwndMain);\r
3456         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3457         if(appData.autoLogo) {\r
3458           \r
3459           switch(gameMode) { // pick logos based on game mode\r
3460             case IcsObserving:\r
3461                 whiteLogo = second.programLogo; // ICS logo\r
3462                 blackLogo = second.programLogo;\r
3463             default:\r
3464                 break;\r
3465             case IcsPlayingWhite:\r
3466                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3467                 blackLogo = second.programLogo; // ICS logo\r
3468                 break;\r
3469             case IcsPlayingBlack:\r
3470                 whiteLogo = second.programLogo; // ICS logo\r
3471                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3472                 break;\r
3473             case TwoMachinesPlay:\r
3474                 if(first.twoMachinesColor[0] == 'b') {\r
3475                     whiteLogo = second.programLogo;\r
3476                     blackLogo = first.programLogo;\r
3477                 }\r
3478                 break;\r
3479             case MachinePlaysWhite:\r
3480                 blackLogo = userLogo;\r
3481                 break;\r
3482             case MachinePlaysBlack:\r
3483                 whiteLogo = userLogo;\r
3484                 blackLogo = first.programLogo;\r
3485           }\r
3486         }\r
3487         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3488         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3489         ReleaseDC(hwndMain, hdc);\r
3490   }\r
3491 }\r
3492 \r
3493 void\r
3494 UpdateLogos(int display)\r
3495 { // called after loading new engine(s), in tourney or from menu\r
3496   LoadLogo(&first, 0, FALSE);\r
3497   LoadLogo(&second, 1, appData.icsActive);\r
3498   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3499   if(display) DisplayLogos();\r
3500 }\r
3501 \r
3502 static HDC hdcSeek;\r
3503 \r
3504 // [HGM] seekgraph\r
3505 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3506 {\r
3507     POINT stPt;\r
3508     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3509     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3510     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3511     SelectObject( hdcSeek, hp );\r
3512 }\r
3513 \r
3514 // front-end wrapper for drawing functions to do rectangles\r
3515 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3516 {\r
3517     HPEN hp;\r
3518     RECT rc;\r
3519 \r
3520     if (hdcSeek == NULL) {\r
3521     hdcSeek = GetDC(hwndMain);\r
3522       if (!appData.monoMode) {\r
3523         SelectPalette(hdcSeek, hPal, FALSE);\r
3524         RealizePalette(hdcSeek);\r
3525       }\r
3526     }\r
3527     hp = SelectObject( hdcSeek, gridPen );\r
3528     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3529     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3530     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3531     SelectObject( hdcSeek, hp );\r
3532 }\r
3533 \r
3534 // front-end wrapper for putting text in graph\r
3535 void DrawSeekText(char *buf, int x, int y)\r
3536 {\r
3537         SIZE stSize;\r
3538         SetBkMode( hdcSeek, TRANSPARENT );\r
3539         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3540         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3541 }\r
3542 \r
3543 void DrawSeekDot(int x, int y, int color)\r
3544 {\r
3545         int square = color & 0x80;\r
3546         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3547                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3548         color &= 0x7F;\r
3549         if(square)\r
3550             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3551                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3552         else\r
3553             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3554                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3555             SelectObject(hdcSeek, oldBrush);\r
3556 }\r
3557 \r
3558 void DrawSeekOpen()\r
3559 {\r
3560 }\r
3561 \r
3562 void DrawSeekClose()\r
3563 {\r
3564 }\r
3565 \r
3566 VOID\r
3567 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3568 {\r
3569   static Board lastReq[2], lastDrawn[2];\r
3570   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3571   static int lastDrawnFlipView = 0;\r
3572   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3573   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3574   HDC tmphdc;\r
3575   HDC hdcmem;\r
3576   HBITMAP bufferBitmap;\r
3577   HBITMAP oldBitmap;\r
3578   RECT Rect;\r
3579   HRGN clips[MAX_CLIPS];\r
3580   ChessSquare dragged_piece = EmptySquare;\r
3581   int nr = twoBoards*partnerUp;\r
3582 \r
3583   /* I'm undecided on this - this function figures out whether a full\r
3584    * repaint is necessary on its own, so there's no real reason to have the\r
3585    * caller tell it that.  I think this can safely be set to FALSE - but\r
3586    * if we trust the callers not to request full repaints unnessesarily, then\r
3587    * we could skip some clipping work.  In other words, only request a full\r
3588    * redraw when the majority of pieces have changed positions (ie. flip, \r
3589    * gamestart and similar)  --Hawk\r
3590    */\r
3591   Boolean fullrepaint = repaint;\r
3592 \r
3593   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3594 \r
3595   if( DrawPositionNeedsFullRepaint() ) {\r
3596       fullrepaint = TRUE;\r
3597   }\r
3598 \r
3599   if (board == NULL) {\r
3600     if (!lastReqValid[nr]) {\r
3601       return;\r
3602     }\r
3603     board = lastReq[nr];\r
3604   } else {\r
3605     CopyBoard(lastReq[nr], board);\r
3606     lastReqValid[nr] = 1;\r
3607   }\r
3608 \r
3609   if (doingSizing) {\r
3610     return;\r
3611   }\r
3612 \r
3613   if (IsIconic(hwndMain)) {\r
3614     return;\r
3615   }\r
3616 \r
3617   if (hdc == NULL) {\r
3618     hdc = GetDC(hwndMain);\r
3619     if (!appData.monoMode) {\r
3620       SelectPalette(hdc, hPal, FALSE);\r
3621       RealizePalette(hdc);\r
3622     }\r
3623     releaseDC = TRUE;\r
3624   } else {\r
3625     releaseDC = FALSE;\r
3626   }\r
3627 \r
3628   /* Create some work-DCs */\r
3629   hdcmem = CreateCompatibleDC(hdc);\r
3630   tmphdc = CreateCompatibleDC(hdc);\r
3631 \r
3632   /* If dragging is in progress, we temporarely remove the piece */\r
3633   /* [HGM] or temporarily decrease count if stacked              */\r
3634   /*       !! Moved to before board compare !!                   */\r
3635   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3636     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3637     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3638             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3639         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3640     } else \r
3641     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3642             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3643         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3644     } else \r
3645         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3646   }\r
3647 \r
3648   /* Figure out which squares need updating by comparing the \r
3649    * newest board with the last drawn board and checking if\r
3650    * flipping has changed.\r
3651    */\r
3652   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3653     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3654       for (column = 0; column < BOARD_WIDTH; column++) {\r
3655         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3656           SquareToPos(row, column, &x, &y);\r
3657           clips[num_clips++] =\r
3658             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3659         }\r
3660       }\r
3661     }\r
3662    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3663     for (i=0; i<2; i++) {\r
3664       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3665           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3666         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3667             lastDrawnHighlight.sq[i].y >= 0) {\r
3668           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3669                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3670           clips[num_clips++] =\r
3671             CreateRectRgn(x - lineGap, y - lineGap, \r
3672                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3673         }\r
3674         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3675           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3676           clips[num_clips++] =\r
3677             CreateRectRgn(x - lineGap, y - lineGap, \r
3678                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3679         }\r
3680       }\r
3681     }\r
3682     for (i=0; i<2; i++) {\r
3683       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3684           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3685         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3686             lastDrawnPremove.sq[i].y >= 0) {\r
3687           SquareToPos(lastDrawnPremove.sq[i].y,\r
3688                       lastDrawnPremove.sq[i].x, &x, &y);\r
3689           clips[num_clips++] =\r
3690             CreateRectRgn(x - lineGap, y - lineGap, \r
3691                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3692         }\r
3693         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3694             premoveHighlightInfo.sq[i].y >= 0) {\r
3695           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3696                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3697           clips[num_clips++] =\r
3698             CreateRectRgn(x - lineGap, y - lineGap, \r
3699                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3700         }\r
3701       }\r
3702     }\r
3703    } else { // nr == 1\r
3704         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3705         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3706         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3707         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3708       for (i=0; i<2; i++) {\r
3709         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3710             partnerHighlightInfo.sq[i].y >= 0) {\r
3711           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3712                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3713           clips[num_clips++] =\r
3714             CreateRectRgn(x - lineGap, y - lineGap, \r
3715                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3716         }\r
3717         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3718             oldPartnerHighlight.sq[i].y >= 0) {\r
3719           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3720                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3721           clips[num_clips++] =\r
3722             CreateRectRgn(x - lineGap, y - lineGap, \r
3723                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3724         }\r
3725       }\r
3726    }\r
3727   } else {\r
3728     fullrepaint = TRUE;\r
3729   }\r
3730 \r
3731   /* Create a buffer bitmap - this is the actual bitmap\r
3732    * being written to.  When all the work is done, we can\r
3733    * copy it to the real DC (the screen).  This avoids\r
3734    * the problems with flickering.\r
3735    */\r
3736   GetClientRect(hwndMain, &Rect);\r
3737   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3738                                         Rect.bottom-Rect.top+1);\r
3739   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3740   if (!appData.monoMode) {\r
3741     SelectPalette(hdcmem, hPal, FALSE);\r
3742   }\r
3743 \r
3744   /* Create clips for dragging */\r
3745   if (!fullrepaint) {\r
3746     if (dragInfo.from.x >= 0) {\r
3747       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3748       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3749     }\r
3750     if (dragInfo.start.x >= 0) {\r
3751       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3752       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3753     }\r
3754     if (dragInfo.pos.x >= 0) {\r
3755       x = dragInfo.pos.x - squareSize / 2;\r
3756       y = dragInfo.pos.y - squareSize / 2;\r
3757       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3758     }\r
3759     if (dragInfo.lastpos.x >= 0) {\r
3760       x = dragInfo.lastpos.x - squareSize / 2;\r
3761       y = dragInfo.lastpos.y - squareSize / 2;\r
3762       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3763     }\r
3764   }\r
3765 \r
3766   /* Are we animating a move?  \r
3767    * If so, \r
3768    *   - remove the piece from the board (temporarely)\r
3769    *   - calculate the clipping region\r
3770    */\r
3771   if (!fullrepaint) {\r
3772     if (animInfo.piece != EmptySquare) {\r
3773       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3774       x = boardRect.left + animInfo.lastpos.x;\r
3775       y = boardRect.top + animInfo.lastpos.y;\r
3776       x2 = boardRect.left + animInfo.pos.x;\r
3777       y2 = boardRect.top + animInfo.pos.y;\r
3778       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3779       /* Slight kludge.  The real problem is that after AnimateMove is\r
3780          done, the position on the screen does not match lastDrawn.\r
3781          This currently causes trouble only on e.p. captures in\r
3782          atomic, where the piece moves to an empty square and then\r
3783          explodes.  The old and new positions both had an empty square\r
3784          at the destination, but animation has drawn a piece there and\r
3785          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3786       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3787     }\r
3788   }\r
3789 \r
3790   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3791   if (num_clips == 0)\r
3792     fullrepaint = TRUE;\r
3793 \r
3794   /* Set clipping on the memory DC */\r
3795   if (!fullrepaint) {\r
3796     SelectClipRgn(hdcmem, clips[0]);\r
3797     for (x = 1; x < num_clips; x++) {\r
3798       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3799         abort();  // this should never ever happen!\r
3800     }\r
3801   }\r
3802 \r
3803   /* Do all the drawing to the memory DC */\r
3804   if(explodeInfo.radius) { // [HGM] atomic\r
3805         HBRUSH oldBrush;\r
3806         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3807         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3808         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3809         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3810         x += squareSize/2;\r
3811         y += squareSize/2;\r
3812         if(!fullrepaint) {\r
3813           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3814           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3815         }\r
3816         DrawGridOnDC(hdcmem);\r
3817         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3818         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3819         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3820         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3821         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3822         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3823         SelectObject(hdcmem, oldBrush);\r
3824   } else {\r
3825     DrawGridOnDC(hdcmem);\r
3826     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3827         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3828         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3829     } else {\r
3830         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3831         oldPartnerHighlight = partnerHighlightInfo;\r
3832     }\r
3833     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3834   }\r
3835   if(nr == 0) // [HGM] dual: markers only on left board\r
3836   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3837     for (column = 0; column < BOARD_WIDTH; column++) {\r
3838         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3839             HBRUSH oldBrush = SelectObject(hdcmem, \r
3840                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3841             SquareToPos(row, column, &x, &y);\r
3842             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3843                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3844             SelectObject(hdcmem, oldBrush);\r
3845         }\r
3846     }\r
3847   }\r
3848 \r
3849   if( appData.highlightMoveWithArrow ) {\r
3850     DrawArrowHighlight(hdcmem);\r
3851   }\r
3852 \r
3853   DrawCoordsOnDC(hdcmem);\r
3854 \r
3855   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3856                  /* to make sure lastDrawn contains what is actually drawn */\r
3857 \r
3858   /* Put the dragged piece back into place and draw it (out of place!) */\r
3859     if (dragged_piece != EmptySquare) {\r
3860     /* [HGM] or restack */\r
3861     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3862                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3863     else\r
3864     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3865                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3866     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3867     x = dragInfo.pos.x - squareSize / 2;\r
3868     y = dragInfo.pos.y - squareSize / 2;\r
3869     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3870                   ((int) dragInfo.piece < (int) BlackPawn), \r
3871                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3872   }   \r
3873   \r
3874   /* Put the animated piece back into place and draw it */\r
3875   if (animInfo.piece != EmptySquare) {\r
3876     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3877     x = boardRect.left + animInfo.pos.x;\r
3878     y = boardRect.top + animInfo.pos.y;\r
3879     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3880                   ((int) animInfo.piece < (int) BlackPawn),\r
3881                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3882   }\r
3883 \r
3884   /* Release the bufferBitmap by selecting in the old bitmap \r
3885    * and delete the memory DC\r
3886    */\r
3887   SelectObject(hdcmem, oldBitmap);\r
3888   DeleteDC(hdcmem);\r
3889 \r
3890   /* Set clipping on the target DC */\r
3891   if (!fullrepaint) {\r
3892     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3893         RECT rect;\r
3894         GetRgnBox(clips[x], &rect);\r
3895         DeleteObject(clips[x]);\r
3896         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3897                           rect.right + wpMain.width/2, rect.bottom);\r
3898     }\r
3899     SelectClipRgn(hdc, clips[0]);\r
3900     for (x = 1; x < num_clips; x++) {\r
3901       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3902         abort();   // this should never ever happen!\r
3903     } \r
3904   }\r
3905 \r
3906   /* Copy the new bitmap onto the screen in one go.\r
3907    * This way we avoid any flickering\r
3908    */\r
3909   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3910   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3911          boardRect.right - boardRect.left,\r
3912          boardRect.bottom - boardRect.top,\r
3913          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3914   if(saveDiagFlag) { \r
3915     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3916     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3917 \r
3918     GetObject(bufferBitmap, sizeof(b), &b);\r
3919     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3920         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3921         bih.biWidth = b.bmWidth;\r
3922         bih.biHeight = b.bmHeight;\r
3923         bih.biPlanes = 1;\r
3924         bih.biBitCount = b.bmBitsPixel;\r
3925         bih.biCompression = 0;\r
3926         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3927         bih.biXPelsPerMeter = 0;\r
3928         bih.biYPelsPerMeter = 0;\r
3929         bih.biClrUsed = 0;\r
3930         bih.biClrImportant = 0;\r
3931 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3932 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3933         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3934 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3935 \r
3936         wb = b.bmWidthBytes;\r
3937         // count colors\r
3938         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3939                 int k = ((int*) pData)[i];\r
3940                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3941                 if(j >= 16) break;\r
3942                 color[j] = k;\r
3943                 if(j >= nrColors) nrColors = j+1;\r
3944         }\r
3945         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3946                 INT p = 0;\r
3947                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3948                     for(w=0; w<(wb>>2); w+=2) {\r
3949                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3950                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3951                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3952                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3953                         pData[p++] = m | j<<4;\r
3954                     }\r
3955                     while(p&3) pData[p++] = 0;\r
3956                 }\r
3957                 fac = 3;\r
3958                 wb = ((wb+31)>>5)<<2;\r
3959         }\r
3960         // write BITMAPFILEHEADER\r
3961         fprintf(diagFile, "BM");\r
3962         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3963         fputDW(diagFile, 0);\r
3964         fputDW(diagFile, 0x36 + (fac?64:0));\r
3965         // write BITMAPINFOHEADER\r
3966         fputDW(diagFile, 40);\r
3967         fputDW(diagFile, b.bmWidth);\r
3968         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3969         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3970         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3971         fputDW(diagFile, 0);\r
3972         fputDW(diagFile, 0);\r
3973         fputDW(diagFile, 0);\r
3974         fputDW(diagFile, 0);\r
3975         fputDW(diagFile, 0);\r
3976         fputDW(diagFile, 0);\r
3977         // write color table\r
3978         if(fac)\r
3979         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3980         // write bitmap data\r
3981         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3982                 fputc(pData[i], diagFile);\r
3983         free(pData);\r
3984      }\r
3985   }\r
3986 \r
3987   SelectObject(tmphdc, oldBitmap);\r
3988 \r
3989   /* Massive cleanup */\r
3990   for (x = 0; x < num_clips; x++)\r
3991     DeleteObject(clips[x]);\r
3992 \r
3993   DeleteDC(tmphdc);\r
3994   DeleteObject(bufferBitmap);\r
3995 \r
3996   if (releaseDC) \r
3997     ReleaseDC(hwndMain, hdc);\r
3998   \r
3999   if (lastDrawnFlipView != flipView && nr == 0) {\r
4000     if (flipView)\r
4001       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4002     else\r
4003       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4004   }\r
4005 \r
4006 /*  CopyBoard(lastDrawn, board);*/\r
4007   lastDrawnHighlight = highlightInfo;\r
4008   lastDrawnPremove   = premoveHighlightInfo;\r
4009   lastDrawnFlipView = flipView;\r
4010   lastDrawnValid[nr] = 1;\r
4011 }\r
4012 \r
4013 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4014 int\r
4015 SaveDiagram(f)\r
4016      FILE *f;\r
4017 {\r
4018     saveDiagFlag = 1; diagFile = f;\r
4019     HDCDrawPosition(NULL, TRUE, NULL);\r
4020     saveDiagFlag = 0;\r
4021 \r
4022     fclose(f);\r
4023     return TRUE;\r
4024 }\r
4025 \r
4026 \r
4027 /*---------------------------------------------------------------------------*\\r
4028 | CLIENT PAINT PROCEDURE\r
4029 |   This is the main event-handler for the WM_PAINT message.\r
4030 |\r
4031 \*---------------------------------------------------------------------------*/\r
4032 VOID\r
4033 PaintProc(HWND hwnd)\r
4034 {\r
4035   HDC         hdc;\r
4036   PAINTSTRUCT ps;\r
4037   HFONT       oldFont;\r
4038 \r
4039   if((hdc = BeginPaint(hwnd, &ps))) {\r
4040     if (IsIconic(hwnd)) {\r
4041       DrawIcon(hdc, 2, 2, iconCurrent);\r
4042     } else {\r
4043       if (!appData.monoMode) {\r
4044         SelectPalette(hdc, hPal, FALSE);\r
4045         RealizePalette(hdc);\r
4046       }\r
4047       HDCDrawPosition(hdc, 1, NULL);\r
4048       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4049         flipView = !flipView; partnerUp = !partnerUp;\r
4050         HDCDrawPosition(hdc, 1, NULL);\r
4051         flipView = !flipView; partnerUp = !partnerUp;\r
4052       }\r
4053       oldFont =\r
4054         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4055       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4056                  ETO_CLIPPED|ETO_OPAQUE,\r
4057                  &messageRect, messageText, strlen(messageText), NULL);\r
4058       SelectObject(hdc, oldFont);\r
4059       DisplayBothClocks();\r
4060       DisplayLogos();\r
4061     }\r
4062     EndPaint(hwnd,&ps);\r
4063   }\r
4064 \r
4065   return;\r
4066 }\r
4067 \r
4068 \r
4069 /*\r
4070  * If the user selects on a border boundary, return -1; if off the board,\r
4071  *   return -2.  Otherwise map the event coordinate to the square.\r
4072  * The offset boardRect.left or boardRect.top must already have been\r
4073  *   subtracted from x.\r
4074  */\r
4075 int EventToSquare(x, limit)\r
4076      int x, limit;\r
4077 {\r
4078   if (x <= 0)\r
4079     return -2;\r
4080   if (x < lineGap)\r
4081     return -1;\r
4082   x -= lineGap;\r
4083   if ((x % (squareSize + lineGap)) >= squareSize)\r
4084     return -1;\r
4085   x /= (squareSize + lineGap);\r
4086     if (x >= limit)\r
4087     return -2;\r
4088   return x;\r
4089 }\r
4090 \r
4091 typedef struct {\r
4092   char piece;\r
4093   int command;\r
4094   char* name;\r
4095 } DropEnable;\r
4096 \r
4097 DropEnable dropEnables[] = {\r
4098   { 'P', DP_Pawn, N_("Pawn") },\r
4099   { 'N', DP_Knight, N_("Knight") },\r
4100   { 'B', DP_Bishop, N_("Bishop") },\r
4101   { 'R', DP_Rook, N_("Rook") },\r
4102   { 'Q', DP_Queen, N_("Queen") },\r
4103 };\r
4104 \r
4105 VOID\r
4106 SetupDropMenu(HMENU hmenu)\r
4107 {\r
4108   int i, count, enable;\r
4109   char *p;\r
4110   extern char white_holding[], black_holding[];\r
4111   char item[MSG_SIZ];\r
4112 \r
4113   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4114     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4115                dropEnables[i].piece);\r
4116     count = 0;\r
4117     while (p && *p++ == dropEnables[i].piece) count++;\r
4118       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4119     enable = count > 0 || !appData.testLegality\r
4120       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4121                       && !appData.icsActive);\r
4122     ModifyMenu(hmenu, dropEnables[i].command,\r
4123                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4124                dropEnables[i].command, item);\r
4125   }\r
4126 }\r
4127 \r
4128 void DragPieceBegin(int x, int y, Boolean instantly)\r
4129 {\r
4130       dragInfo.lastpos.x = boardRect.left + x;\r
4131       dragInfo.lastpos.y = boardRect.top + y;\r
4132       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4133       dragInfo.from.x = fromX;\r
4134       dragInfo.from.y = fromY;\r
4135       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4136       dragInfo.start = dragInfo.from;\r
4137       SetCapture(hwndMain);\r
4138 }\r
4139 \r
4140 void DragPieceEnd(int x, int y)\r
4141 {\r
4142     ReleaseCapture();\r
4143     dragInfo.start.x = dragInfo.start.y = -1;\r
4144     dragInfo.from = dragInfo.start;\r
4145     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4146 }\r
4147 \r
4148 void ChangeDragPiece(ChessSquare piece)\r
4149 {\r
4150     dragInfo.piece = piece;\r
4151 }\r
4152 \r
4153 /* Event handler for mouse messages */\r
4154 VOID\r
4155 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4156 {\r
4157   int x, y, menuNr;\r
4158   POINT pt;\r
4159   static int recursive = 0;\r
4160   HMENU hmenu;\r
4161   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4162 \r
4163   if (recursive) {\r
4164     if (message == WM_MBUTTONUP) {\r
4165       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4166          to the middle button: we simulate pressing the left button too!\r
4167          */\r
4168       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4169       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4170     }\r
4171     return;\r
4172   }\r
4173   recursive++;\r
4174   \r
4175   pt.x = LOWORD(lParam);\r
4176   pt.y = HIWORD(lParam);\r
4177   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4178   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4179   if (!flipView && y >= 0) {\r
4180     y = BOARD_HEIGHT - 1 - y;\r
4181   }\r
4182   if (flipView && x >= 0) {\r
4183     x = BOARD_WIDTH - 1 - x;\r
4184   }\r
4185 \r
4186   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4187 \r
4188   switch (message) {\r
4189   case WM_LBUTTONDOWN:\r
4190       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4191         ClockClick(flipClock); break;\r
4192       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4193         ClockClick(!flipClock); break;\r
4194       }\r
4195       dragInfo.start.x = dragInfo.start.y = -1;\r
4196       dragInfo.from = dragInfo.start;\r
4197     if(fromX == -1 && frozen) { // not sure where this is for\r
4198                 fromX = fromY = -1; \r
4199       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4200       break;\r
4201     }\r
4202       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4203       DrawPosition(TRUE, NULL);\r
4204     break;\r
4205 \r
4206   case WM_LBUTTONUP:\r
4207       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4208       DrawPosition(TRUE, NULL);\r
4209     break;\r
4210 \r
4211   case WM_MOUSEMOVE:\r
4212     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4213     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4214     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4215     if ((appData.animateDragging || appData.highlightDragging)\r
4216         && (wParam & MK_LBUTTON)\r
4217         && dragInfo.from.x >= 0) \r
4218     {\r
4219       BOOL full_repaint = FALSE;\r
4220 \r
4221       if (appData.animateDragging) {\r
4222         dragInfo.pos = pt;\r
4223       }\r
4224       if (appData.highlightDragging) {\r
4225         SetHighlights(fromX, fromY, x, y);\r
4226         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4227             full_repaint = TRUE;\r
4228         }\r
4229       }\r
4230       \r
4231       DrawPosition( full_repaint, NULL);\r
4232       \r
4233       dragInfo.lastpos = dragInfo.pos;\r
4234     }\r
4235     break;\r
4236 \r
4237   case WM_MOUSEWHEEL: // [DM]\r
4238     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4239        /* Mouse Wheel is being rolled forward\r
4240         * Play moves forward\r
4241         */\r
4242        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4243                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4244        /* Mouse Wheel is being rolled backward\r
4245         * Play moves backward\r
4246         */\r
4247        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4248                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4249     }\r
4250     break;\r
4251 \r
4252   case WM_MBUTTONUP:\r
4253   case WM_RBUTTONUP:\r
4254     ReleaseCapture();\r
4255     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4256     break;\r
4257  \r
4258   case WM_MBUTTONDOWN:\r
4259   case WM_RBUTTONDOWN:\r
4260     ErrorPopDown();\r
4261     ReleaseCapture();\r
4262     fromX = fromY = -1;\r
4263     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4264     dragInfo.start.x = dragInfo.start.y = -1;\r
4265     dragInfo.from = dragInfo.start;\r
4266     dragInfo.lastpos = dragInfo.pos;\r
4267     if (appData.highlightDragging) {\r
4268       ClearHighlights();\r
4269     }\r
4270     if(y == -2) {\r
4271       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4272       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4273           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4274       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4275           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4276       }\r
4277       break;\r
4278     }\r
4279     DrawPosition(TRUE, NULL);\r
4280 \r
4281     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4282     switch (menuNr) {\r
4283     case 0:\r
4284       if (message == WM_MBUTTONDOWN) {\r
4285         buttonCount = 3;  /* even if system didn't think so */\r
4286         if (wParam & MK_SHIFT) \r
4287           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4288         else\r
4289           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4290       } else { /* message == WM_RBUTTONDOWN */\r
4291         /* Just have one menu, on the right button.  Windows users don't\r
4292            think to try the middle one, and sometimes other software steals\r
4293            it, or it doesn't really exist. */\r
4294         if(gameInfo.variant != VariantShogi)\r
4295             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4296         else\r
4297             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4298       }\r
4299       break;\r
4300     case 2:\r
4301       SetCapture(hwndMain);\r
4302       break;\r
4303     case 1:\r
4304       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4305       SetupDropMenu(hmenu);\r
4306       MenuPopup(hwnd, pt, hmenu, -1);\r
4307     default:\r
4308       break;\r
4309     }\r
4310     break;\r
4311   }\r
4312 \r
4313   recursive--;\r
4314 }\r
4315 \r
4316 /* Preprocess messages for buttons in main window */\r
4317 LRESULT CALLBACK\r
4318 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4319 {\r
4320   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4321   int i, dir;\r
4322 \r
4323   for (i=0; i<N_BUTTONS; i++) {\r
4324     if (buttonDesc[i].id == id) break;\r
4325   }\r
4326   if (i == N_BUTTONS) return 0;\r
4327   switch (message) {\r
4328   case WM_KEYDOWN:\r
4329     switch (wParam) {\r
4330     case VK_LEFT:\r
4331     case VK_RIGHT:\r
4332       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4333       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4334       return TRUE;\r
4335     }\r
4336     break;\r
4337   case WM_CHAR:\r
4338     switch (wParam) {\r
4339     case '\r':\r
4340       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4341       return TRUE;\r
4342     default:\r
4343       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4344         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4345         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4346         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4347         SetFocus(h);\r
4348         SendMessage(h, WM_CHAR, wParam, lParam);\r
4349         return TRUE;\r
4350       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4351         TypeInEvent((char)wParam);\r
4352       }\r
4353       break;\r
4354     }\r
4355     break;\r
4356   }\r
4357   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4358 }\r
4359 \r
4360 /* Process messages for Promotion dialog box */\r
4361 LRESULT CALLBACK\r
4362 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4363 {\r
4364   char promoChar;\r
4365 \r
4366   switch (message) {\r
4367   case WM_INITDIALOG: /* message: initialize dialog box */\r
4368     /* Center the dialog over the application window */\r
4369     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4370     Translate(hDlg, DLG_PromotionKing);\r
4371     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4372       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4373        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4374        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4375                SW_SHOW : SW_HIDE);\r
4376     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4377     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4378        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4379          PieceToChar(WhiteAngel) != '~') ||\r
4380         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4381          PieceToChar(BlackAngel) != '~')   ) ?\r
4382                SW_SHOW : SW_HIDE);\r
4383     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4384        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4385          PieceToChar(WhiteMarshall) != '~') ||\r
4386         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4387          PieceToChar(BlackMarshall) != '~')   ) ?\r
4388                SW_SHOW : SW_HIDE);\r
4389     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4390     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4391        gameInfo.variant != VariantShogi ?\r
4392                SW_SHOW : SW_HIDE);\r
4393     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4394        gameInfo.variant != VariantShogi ?\r
4395                SW_SHOW : SW_HIDE);\r
4396     if(gameInfo.variant == VariantShogi) {\r
4397         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4398         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4399         SetWindowText(hDlg, "Promote?");\r
4400     }\r
4401     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4402        gameInfo.variant == VariantSuper ?\r
4403                SW_SHOW : SW_HIDE);\r
4404     return TRUE;\r
4405 \r
4406   case WM_COMMAND: /* message: received a command */\r
4407     switch (LOWORD(wParam)) {\r
4408     case IDCANCEL:\r
4409       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4410       ClearHighlights();\r
4411       DrawPosition(FALSE, NULL);\r
4412       return TRUE;\r
4413     case PB_King:\r
4414       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4415       break;\r
4416     case PB_Queen:\r
4417       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4418       break;\r
4419     case PB_Rook:\r
4420       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4421       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4422       break;\r
4423     case PB_Bishop:\r
4424       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4425       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4426       break;\r
4427     case PB_Chancellor:\r
4428       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4429       break;\r
4430     case PB_Archbishop:\r
4431       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4432       break;\r
4433     case PB_Knight:\r
4434       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4435       break;\r
4436     default:\r
4437       return FALSE;\r
4438     }\r
4439     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4440     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4441     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4442     fromX = fromY = -1;\r
4443     if (!appData.highlightLastMove) {\r
4444       ClearHighlights();\r
4445       DrawPosition(FALSE, NULL);\r
4446     }\r
4447     return TRUE;\r
4448   }\r
4449   return FALSE;\r
4450 }\r
4451 \r
4452 /* Pop up promotion dialog */\r
4453 VOID\r
4454 PromotionPopup(HWND hwnd)\r
4455 {\r
4456   FARPROC lpProc;\r
4457 \r
4458   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4459   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4460     hwnd, (DLGPROC)lpProc);\r
4461   FreeProcInstance(lpProc);\r
4462 }\r
4463 \r
4464 void\r
4465 PromotionPopUp()\r
4466 {\r
4467   DrawPosition(TRUE, NULL);\r
4468   PromotionPopup(hwndMain);\r
4469 }\r
4470 \r
4471 VOID\r
4472 LoadGameDialog(HWND hwnd, char* title)\r
4473 {\r
4474   UINT number = 0;\r
4475   FILE *f;\r
4476   char fileTitle[MSG_SIZ];\r
4477   f = OpenFileDialog(hwnd, "rb", "",\r
4478                      appData.oldSaveStyle ? "gam" : "pgn",\r
4479                      GAME_FILT,\r
4480                      title, &number, fileTitle, NULL);\r
4481   if (f != NULL) {\r
4482     cmailMsgLoaded = FALSE;\r
4483     if (number == 0) {\r
4484       int error = GameListBuild(f);\r
4485       if (error) {\r
4486         DisplayError(_("Cannot build game list"), error);\r
4487       } else if (!ListEmpty(&gameList) &&\r
4488                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4489         GameListPopUp(f, fileTitle);\r
4490         return;\r
4491       }\r
4492       GameListDestroy();\r
4493       number = 1;\r
4494     }\r
4495     LoadGame(f, number, fileTitle, FALSE);\r
4496   }\r
4497 }\r
4498 \r
4499 int get_term_width()\r
4500 {\r
4501     HDC hdc;\r
4502     TEXTMETRIC tm;\r
4503     RECT rc;\r
4504     HFONT hfont, hold_font;\r
4505     LOGFONT lf;\r
4506     HWND hText;\r
4507 \r
4508     if (hwndConsole)\r
4509         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4510     else\r
4511         return 79;\r
4512 \r
4513     // get the text metrics\r
4514     hdc = GetDC(hText);\r
4515     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4516     if (consoleCF.dwEffects & CFE_BOLD)\r
4517         lf.lfWeight = FW_BOLD;\r
4518     if (consoleCF.dwEffects & CFE_ITALIC)\r
4519         lf.lfItalic = TRUE;\r
4520     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4521         lf.lfStrikeOut = TRUE;\r
4522     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4523         lf.lfUnderline = TRUE;\r
4524     hfont = CreateFontIndirect(&lf);\r
4525     hold_font = SelectObject(hdc, hfont);\r
4526     GetTextMetrics(hdc, &tm);\r
4527     SelectObject(hdc, hold_font);\r
4528     DeleteObject(hfont);\r
4529     ReleaseDC(hText, hdc);\r
4530 \r
4531     // get the rectangle\r
4532     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4533 \r
4534     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4535 }\r
4536 \r
4537 void UpdateICSWidth(HWND hText)\r
4538 {\r
4539     LONG old_width, new_width;\r
4540 \r
4541     new_width = get_term_width(hText, FALSE);\r
4542     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4543     if (new_width != old_width)\r
4544     {\r
4545         ics_update_width(new_width);\r
4546         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4547     }\r
4548 }\r
4549 \r
4550 VOID\r
4551 ChangedConsoleFont()\r
4552 {\r
4553   CHARFORMAT cfmt;\r
4554   CHARRANGE tmpsel, sel;\r
4555   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4556   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4557   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4558   PARAFORMAT paraf;\r
4559 \r
4560   cfmt.cbSize = sizeof(CHARFORMAT);\r
4561   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4562     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4563                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4564   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4565    * size.  This was undocumented in the version of MSVC++ that I had\r
4566    * when I wrote the code, but is apparently documented now.\r
4567    */\r
4568   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4569   cfmt.bCharSet = f->lf.lfCharSet;\r
4570   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4571   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4572   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4573   /* Why are the following seemingly needed too? */\r
4574   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4575   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4576   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4577   tmpsel.cpMin = 0;\r
4578   tmpsel.cpMax = -1; /*999999?*/\r
4579   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4580   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4581   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4582    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4583    */\r
4584   paraf.cbSize = sizeof(paraf);\r
4585   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4586   paraf.dxStartIndent = 0;\r
4587   paraf.dxOffset = WRAP_INDENT;\r
4588   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4589   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4590   UpdateICSWidth(hText);\r
4591 }\r
4592 \r
4593 /*---------------------------------------------------------------------------*\\r
4594  *\r
4595  * Window Proc for main window\r
4596  *\r
4597 \*---------------------------------------------------------------------------*/\r
4598 \r
4599 /* Process messages for main window, etc. */\r
4600 LRESULT CALLBACK\r
4601 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4602 {\r
4603   FARPROC lpProc;\r
4604   int wmId, wmEvent;\r
4605   char *defName;\r
4606   FILE *f;\r
4607   UINT number;\r
4608   char fileTitle[MSG_SIZ];\r
4609   char buf[MSG_SIZ];\r
4610   static SnapData sd;\r
4611   static int peek=0;\r
4612 \r
4613   switch (message) {\r
4614 \r
4615   case WM_PAINT: /* message: repaint portion of window */\r
4616     PaintProc(hwnd);\r
4617     break;\r
4618 \r
4619   case WM_ERASEBKGND:\r
4620     if (IsIconic(hwnd)) {\r
4621       /* Cheat; change the message */\r
4622       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4623     } else {\r
4624       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4625     }\r
4626     break;\r
4627 \r
4628   case WM_LBUTTONDOWN:\r
4629   case WM_MBUTTONDOWN:\r
4630   case WM_RBUTTONDOWN:\r
4631   case WM_LBUTTONUP:\r
4632   case WM_MBUTTONUP:\r
4633   case WM_RBUTTONUP:\r
4634   case WM_MOUSEMOVE:\r
4635   case WM_MOUSEWHEEL:\r
4636     MouseEvent(hwnd, message, wParam, lParam);\r
4637     break;\r
4638 \r
4639   case WM_KEYUP:\r
4640     if((char)wParam == '\b') {\r
4641       ForwardEvent(); peek = 0;\r
4642     }\r
4643 \r
4644     JAWS_KBUP_NAVIGATION\r
4645 \r
4646     break;\r
4647 \r
4648   case WM_KEYDOWN:\r
4649     if((char)wParam == '\b') {\r
4650       if(!peek) BackwardEvent(), peek = 1;\r
4651     }\r
4652 \r
4653     JAWS_KBDOWN_NAVIGATION\r
4654 \r
4655     break;\r
4656 \r
4657   case WM_CHAR:\r
4658     \r
4659     JAWS_ALT_INTERCEPT\r
4660 \r
4661     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4662         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4663         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4664         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4665         SetFocus(h);\r
4666         SendMessage(h, message, wParam, lParam);\r
4667     } else if(lParam != KF_REPEAT) {\r
4668         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4669                 TypeInEvent((char)wParam);\r
4670         } else if((char)wParam == 003) CopyGameToClipboard();\r
4671          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4672     }\r
4673 \r
4674     break;\r
4675 \r
4676   case WM_PALETTECHANGED:\r
4677     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4678       int nnew;\r
4679       HDC hdc = GetDC(hwndMain);\r
4680       SelectPalette(hdc, hPal, TRUE);\r
4681       nnew = RealizePalette(hdc);\r
4682       if (nnew > 0) {\r
4683         paletteChanged = TRUE;\r
4684         InvalidateRect(hwnd, &boardRect, FALSE);\r
4685       }\r
4686       ReleaseDC(hwnd, hdc);\r
4687     }\r
4688     break;\r
4689 \r
4690   case WM_QUERYNEWPALETTE:\r
4691     if (!appData.monoMode /*&& paletteChanged*/) {\r
4692       int nnew;\r
4693       HDC hdc = GetDC(hwndMain);\r
4694       paletteChanged = FALSE;\r
4695       SelectPalette(hdc, hPal, FALSE);\r
4696       nnew = RealizePalette(hdc);\r
4697       if (nnew > 0) {\r
4698         InvalidateRect(hwnd, &boardRect, FALSE);\r
4699       }\r
4700       ReleaseDC(hwnd, hdc);\r
4701       return TRUE;\r
4702     }\r
4703     return FALSE;\r
4704 \r
4705   case WM_COMMAND: /* message: command from application menu */\r
4706     wmId    = LOWORD(wParam);\r
4707     wmEvent = HIWORD(wParam);\r
4708 \r
4709     switch (wmId) {\r
4710     case IDM_NewGame:\r
4711       ResetGameEvent();\r
4712       SAY("new game enter a move to play against the computer with white");\r
4713       break;\r
4714 \r
4715     case IDM_NewGameFRC:\r
4716       if( NewGameFRC() == 0 ) {\r
4717         ResetGameEvent();\r
4718       }\r
4719       break;\r
4720 \r
4721     case IDM_NewVariant:\r
4722       NewVariantPopup(hwnd);\r
4723       break;\r
4724 \r
4725     case IDM_LoadGame:\r
4726       LoadGameDialog(hwnd, _("Load Game from File"));\r
4727       break;\r
4728 \r
4729     case IDM_LoadNextGame:\r
4730       ReloadGame(1);\r
4731       break;\r
4732 \r
4733     case IDM_LoadPrevGame:\r
4734       ReloadGame(-1);\r
4735       break;\r
4736 \r
4737     case IDM_ReloadGame:\r
4738       ReloadGame(0);\r
4739       break;\r
4740 \r
4741     case IDM_LoadPosition:\r
4742       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4743         Reset(FALSE, TRUE);\r
4744       }\r
4745       number = 1;\r
4746       f = OpenFileDialog(hwnd, "rb", "",\r
4747                          appData.oldSaveStyle ? "pos" : "fen",\r
4748                          POSITION_FILT,\r
4749                          _("Load Position from File"), &number, fileTitle, NULL);\r
4750       if (f != NULL) {\r
4751         LoadPosition(f, number, fileTitle);\r
4752       }\r
4753       break;\r
4754 \r
4755     case IDM_LoadNextPosition:\r
4756       ReloadPosition(1);\r
4757       break;\r
4758 \r
4759     case IDM_LoadPrevPosition:\r
4760       ReloadPosition(-1);\r
4761       break;\r
4762 \r
4763     case IDM_ReloadPosition:\r
4764       ReloadPosition(0);\r
4765       break;\r
4766 \r
4767     case IDM_SaveGame:\r
4768       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4769       f = OpenFileDialog(hwnd, "a", defName,\r
4770                          appData.oldSaveStyle ? "gam" : "pgn",\r
4771                          GAME_FILT,\r
4772                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4773       if (f != NULL) {\r
4774         SaveGame(f, 0, "");\r
4775       }\r
4776       break;\r
4777 \r
4778     case IDM_SavePosition:\r
4779       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4780       f = OpenFileDialog(hwnd, "a", defName,\r
4781                          appData.oldSaveStyle ? "pos" : "fen",\r
4782                          POSITION_FILT,\r
4783                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4784       if (f != NULL) {\r
4785         SavePosition(f, 0, "");\r
4786       }\r
4787       break;\r
4788 \r
4789     case IDM_SaveDiagram:\r
4790       defName = "diagram";\r
4791       f = OpenFileDialog(hwnd, "wb", defName,\r
4792                          "bmp",\r
4793                          DIAGRAM_FILT,\r
4794                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4795       if (f != NULL) {\r
4796         SaveDiagram(f);\r
4797       }\r
4798       break;\r
4799 \r
4800     case IDM_CopyGame:\r
4801       CopyGameToClipboard();\r
4802       break;\r
4803 \r
4804     case IDM_PasteGame:\r
4805       PasteGameFromClipboard();\r
4806       break;\r
4807 \r
4808     case IDM_CopyGameListToClipboard:\r
4809       CopyGameListToClipboard();\r
4810       break;\r
4811 \r
4812     /* [AS] Autodetect FEN or PGN data */\r
4813     case IDM_PasteAny:\r
4814       PasteGameOrFENFromClipboard();\r
4815       break;\r
4816 \r
4817     /* [AS] Move history */\r
4818     case IDM_ShowMoveHistory:\r
4819         if( MoveHistoryIsUp() ) {\r
4820             MoveHistoryPopDown();\r
4821         }\r
4822         else {\r
4823             MoveHistoryPopUp();\r
4824         }\r
4825         break;\r
4826 \r
4827     /* [AS] Eval graph */\r
4828     case IDM_ShowEvalGraph:\r
4829         if( EvalGraphIsUp() ) {\r
4830             EvalGraphPopDown();\r
4831         }\r
4832         else {\r
4833             EvalGraphPopUp();\r
4834             SetFocus(hwndMain);\r
4835         }\r
4836         break;\r
4837 \r
4838     /* [AS] Engine output */\r
4839     case IDM_ShowEngineOutput:\r
4840         if( EngineOutputIsUp() ) {\r
4841             EngineOutputPopDown();\r
4842         }\r
4843         else {\r
4844             EngineOutputPopUp();\r
4845         }\r
4846         break;\r
4847 \r
4848     /* [AS] User adjudication */\r
4849     case IDM_UserAdjudication_White:\r
4850         UserAdjudicationEvent( +1 );\r
4851         break;\r
4852 \r
4853     case IDM_UserAdjudication_Black:\r
4854         UserAdjudicationEvent( -1 );\r
4855         break;\r
4856 \r
4857     case IDM_UserAdjudication_Draw:\r
4858         UserAdjudicationEvent( 0 );\r
4859         break;\r
4860 \r
4861     /* [AS] Game list options dialog */\r
4862     case IDM_GameListOptions:\r
4863       GameListOptions();\r
4864       break;\r
4865 \r
4866     case IDM_NewChat:\r
4867       ChatPopUp(NULL);\r
4868       break;\r
4869 \r
4870     case IDM_CopyPosition:\r
4871       CopyFENToClipboard();\r
4872       break;\r
4873 \r
4874     case IDM_PastePosition:\r
4875       PasteFENFromClipboard();\r
4876       break;\r
4877 \r
4878     case IDM_MailMove:\r
4879       MailMoveEvent();\r
4880       break;\r
4881 \r
4882     case IDM_ReloadCMailMsg:\r
4883       Reset(TRUE, TRUE);\r
4884       ReloadCmailMsgEvent(FALSE);\r
4885       break;\r
4886 \r
4887     case IDM_Minimize:\r
4888       ShowWindow(hwnd, SW_MINIMIZE);\r
4889       break;\r
4890 \r
4891     case IDM_Exit:\r
4892       ExitEvent(0);\r
4893       break;\r
4894 \r
4895     case IDM_MachineWhite:\r
4896       MachineWhiteEvent();\r
4897       /*\r
4898        * refresh the tags dialog only if it's visible\r
4899        */\r
4900       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4901           char *tags;\r
4902           tags = PGNTags(&gameInfo);\r
4903           TagsPopUp(tags, CmailMsg());\r
4904           free(tags);\r
4905       }\r
4906       SAY("computer starts playing white");\r
4907       break;\r
4908 \r
4909     case IDM_MachineBlack:\r
4910       MachineBlackEvent();\r
4911       /*\r
4912        * refresh the tags dialog only if it's visible\r
4913        */\r
4914       if (gameMode == MachinePlaysBlack && 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 black");\r
4921       break;\r
4922 \r
4923     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4924       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4925       break;\r
4926 \r
4927     case IDM_TwoMachines:\r
4928       TwoMachinesEvent();\r
4929       /*\r
4930        * refresh the tags dialog only if it's visible\r
4931        */\r
4932       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4933           char *tags;\r
4934           tags = PGNTags(&gameInfo);\r
4935           TagsPopUp(tags, CmailMsg());\r
4936           free(tags);\r
4937       }\r
4938       SAY("computer starts playing both sides");\r
4939       break;\r
4940 \r
4941     case IDM_AnalysisMode:\r
4942       if(AnalyzeModeEvent()) {\r
4943         SAY("analyzing current position");\r
4944       }\r
4945       break;\r
4946 \r
4947     case IDM_AnalyzeFile:\r
4948       AnalyzeFileEvent();\r
4949       break;\r
4950 \r
4951     case IDM_IcsClient:\r
4952       IcsClientEvent();\r
4953       break;\r
4954 \r
4955     case IDM_EditGame:\r
4956     case IDM_EditGame2:\r
4957       EditGameEvent();\r
4958       SAY("edit game");\r
4959       break;\r
4960 \r
4961     case IDM_EditPosition:\r
4962     case IDM_EditPosition2:\r
4963       EditPositionEvent();\r
4964       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4965       break;\r
4966 \r
4967     case IDM_Training:\r
4968       TrainingEvent();\r
4969       break;\r
4970 \r
4971     case IDM_ShowGameList:\r
4972       ShowGameListProc();\r
4973       break;\r
4974 \r
4975     case IDM_EditProgs1:\r
4976       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4977       break;\r
4978 \r
4979     case IDM_LoadProg1:\r
4980      LoadEnginePopUp(hwndMain, 0);\r
4981       break;\r
4982 \r
4983     case IDM_LoadProg2:\r
4984      LoadEnginePopUp(hwndMain, 1);\r
4985       break;\r
4986 \r
4987     case IDM_EditServers:\r
4988       EditTagsPopUp(icsNames, &icsNames);\r
4989       break;\r
4990 \r
4991     case IDM_EditTags:\r
4992     case IDM_Tags:\r
4993       EditTagsProc();\r
4994       break;\r
4995 \r
4996     case IDM_EditBook:\r
4997       EditBookEvent();\r
4998       break;\r
4999 \r
5000     case IDM_EditComment:\r
5001     case IDM_Comment:\r
5002       if (commentUp && editComment) {\r
5003         CommentPopDown();\r
5004       } else {\r
5005         EditCommentEvent();\r
5006       }\r
5007       break;\r
5008 \r
5009     case IDM_Pause:\r
5010       PauseEvent();\r
5011       break;\r
5012 \r
5013     case IDM_Accept:\r
5014       AcceptEvent();\r
5015       break;\r
5016 \r
5017     case IDM_Decline:\r
5018       DeclineEvent();\r
5019       break;\r
5020 \r
5021     case IDM_Rematch:\r
5022       RematchEvent();\r
5023       break;\r
5024 \r
5025     case IDM_CallFlag:\r
5026       CallFlagEvent();\r
5027       break;\r
5028 \r
5029     case IDM_Draw:\r
5030       DrawEvent();\r
5031       break;\r
5032 \r
5033     case IDM_Adjourn:\r
5034       AdjournEvent();\r
5035       break;\r
5036 \r
5037     case IDM_Abort:\r
5038       AbortEvent();\r
5039       break;\r
5040 \r
5041     case IDM_Resign:\r
5042       ResignEvent();\r
5043       break;\r
5044 \r
5045     case IDM_StopObserving:\r
5046       StopObservingEvent();\r
5047       break;\r
5048 \r
5049     case IDM_StopExamining:\r
5050       StopExaminingEvent();\r
5051       break;\r
5052 \r
5053     case IDM_Upload:\r
5054       UploadGameEvent();\r
5055       break;\r
5056 \r
5057     case IDM_TypeInMove:\r
5058       TypeInEvent('\000');\r
5059       break;\r
5060 \r
5061     case IDM_TypeInName:\r
5062       PopUpNameDialog('\000');\r
5063       break;\r
5064 \r
5065     case IDM_Backward:\r
5066       BackwardEvent();\r
5067       SetFocus(hwndMain);\r
5068       break;\r
5069 \r
5070     JAWS_MENU_ITEMS\r
5071 \r
5072     case IDM_Forward:\r
5073       ForwardEvent();\r
5074       SetFocus(hwndMain);\r
5075       break;\r
5076 \r
5077     case IDM_ToStart:\r
5078       ToStartEvent();\r
5079       SetFocus(hwndMain);\r
5080       break;\r
5081 \r
5082     case IDM_ToEnd:\r
5083       ToEndEvent();\r
5084       SetFocus(hwndMain);\r
5085       break;\r
5086 \r
5087     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5088     case OPT_GameListPrev:\r
5089       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5090       break;\r
5091 \r
5092     case IDM_Revert:\r
5093       RevertEvent(FALSE);\r
5094       break;\r
5095 \r
5096     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5097       RevertEvent(TRUE);\r
5098       break;\r
5099 \r
5100     case IDM_TruncateGame:\r
5101       TruncateGameEvent();\r
5102       break;\r
5103 \r
5104     case IDM_MoveNow:\r
5105       MoveNowEvent();\r
5106       break;\r
5107 \r
5108     case IDM_RetractMove:\r
5109       RetractMoveEvent();\r
5110       break;\r
5111 \r
5112     case IDM_FlipView:\r
5113       flipView = !flipView;\r
5114       DrawPosition(FALSE, NULL);\r
5115       break;\r
5116 \r
5117     case IDM_FlipClock:\r
5118       flipClock = !flipClock;\r
5119       DisplayBothClocks();\r
5120       DisplayLogos();\r
5121       break;\r
5122 \r
5123     case IDM_MuteSounds:\r
5124       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5125       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5126                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5127       break;\r
5128 \r
5129     case IDM_GeneralOptions:\r
5130       GeneralOptionsPopup(hwnd);\r
5131       DrawPosition(TRUE, NULL);\r
5132       break;\r
5133 \r
5134     case IDM_BoardOptions:\r
5135       BoardOptionsPopup(hwnd);\r
5136       break;\r
5137 \r
5138     case IDM_EnginePlayOptions:\r
5139       EnginePlayOptionsPopup(hwnd);\r
5140       break;\r
5141 \r
5142     case IDM_Engine1Options:\r
5143       EngineOptionsPopup(hwnd, &first);\r
5144       break;\r
5145 \r
5146     case IDM_Engine2Options:\r
5147       savedHwnd = hwnd;\r
5148       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5149       EngineOptionsPopup(hwnd, &second);\r
5150       break;\r
5151 \r
5152     case IDM_OptionsUCI:\r
5153       UciOptionsPopup(hwnd);\r
5154       break;\r
5155 \r
5156     case IDM_Tourney:\r
5157       TourneyPopup(hwnd);\r
5158       break;\r
5159 \r
5160     case IDM_IcsOptions:\r
5161       IcsOptionsPopup(hwnd);\r
5162       break;\r
5163 \r
5164     case IDM_Fonts:\r
5165       FontsOptionsPopup(hwnd);\r
5166       break;\r
5167 \r
5168     case IDM_Sounds:\r
5169       SoundOptionsPopup(hwnd);\r
5170       break;\r
5171 \r
5172     case IDM_CommPort:\r
5173       CommPortOptionsPopup(hwnd);\r
5174       break;\r
5175 \r
5176     case IDM_LoadOptions:\r
5177       LoadOptionsPopup(hwnd);\r
5178       break;\r
5179 \r
5180     case IDM_SaveOptions:\r
5181       SaveOptionsPopup(hwnd);\r
5182       break;\r
5183 \r
5184     case IDM_TimeControl:\r
5185       TimeControlOptionsPopup(hwnd);\r
5186       break;\r
5187 \r
5188     case IDM_SaveSettings:\r
5189       SaveSettings(settingsFileName);\r
5190       break;\r
5191 \r
5192     case IDM_SaveSettingsOnExit:\r
5193       saveSettingsOnExit = !saveSettingsOnExit;\r
5194       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5195                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5196                                          MF_CHECKED : MF_UNCHECKED));\r
5197       break;\r
5198 \r
5199     case IDM_Hint:\r
5200       HintEvent();\r
5201       break;\r
5202 \r
5203     case IDM_Book:\r
5204       BookEvent();\r
5205       break;\r
5206 \r
5207     case IDM_AboutGame:\r
5208       AboutGameEvent();\r
5209       break;\r
5210 \r
5211     case IDM_Debug:\r
5212       appData.debugMode = !appData.debugMode;\r
5213       if (appData.debugMode) {\r
5214         char dir[MSG_SIZ];\r
5215         GetCurrentDirectory(MSG_SIZ, dir);\r
5216         SetCurrentDirectory(installDir);\r
5217         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5218         SetCurrentDirectory(dir);\r
5219         setbuf(debugFP, NULL);\r
5220       } else {\r
5221         fclose(debugFP);\r
5222         debugFP = NULL;\r
5223       }\r
5224       break;\r
5225 \r
5226     case IDM_HELPCONTENTS:\r
5227       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5228           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5229           MessageBox (GetFocus(),\r
5230                     _("Unable to activate help"),\r
5231                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5232       }\r
5233       break;\r
5234 \r
5235     case IDM_HELPSEARCH:\r
5236         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5237             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5238         MessageBox (GetFocus(),\r
5239                     _("Unable to activate help"),\r
5240                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5241       }\r
5242       break;\r
5243 \r
5244     case IDM_HELPHELP:\r
5245       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5246         MessageBox (GetFocus(),\r
5247                     _("Unable to activate help"),\r
5248                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5249       }\r
5250       break;\r
5251 \r
5252     case IDM_ABOUT:\r
5253       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5254       DialogBox(hInst, \r
5255         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5256         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5257       FreeProcInstance(lpProc);\r
5258       break;\r
5259 \r
5260     case IDM_DirectCommand1:\r
5261       AskQuestionEvent(_("Direct Command"),\r
5262                        _("Send to chess program:"), "", "1");\r
5263       break;\r
5264     case IDM_DirectCommand2:\r
5265       AskQuestionEvent(_("Direct Command"),\r
5266                        _("Send to second chess program:"), "", "2");\r
5267       break;\r
5268 \r
5269     case EP_WhitePawn:\r
5270       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5271       fromX = fromY = -1;\r
5272       break;\r
5273 \r
5274     case EP_WhiteKnight:\r
5275       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5276       fromX = fromY = -1;\r
5277       break;\r
5278 \r
5279     case EP_WhiteBishop:\r
5280       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5281       fromX = fromY = -1;\r
5282       break;\r
5283 \r
5284     case EP_WhiteRook:\r
5285       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5286       fromX = fromY = -1;\r
5287       break;\r
5288 \r
5289     case EP_WhiteQueen:\r
5290       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5291       fromX = fromY = -1;\r
5292       break;\r
5293 \r
5294     case EP_WhiteFerz:\r
5295       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5296       fromX = fromY = -1;\r
5297       break;\r
5298 \r
5299     case EP_WhiteWazir:\r
5300       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5301       fromX = fromY = -1;\r
5302       break;\r
5303 \r
5304     case EP_WhiteAlfil:\r
5305       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5306       fromX = fromY = -1;\r
5307       break;\r
5308 \r
5309     case EP_WhiteCannon:\r
5310       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5311       fromX = fromY = -1;\r
5312       break;\r
5313 \r
5314     case EP_WhiteCardinal:\r
5315       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5316       fromX = fromY = -1;\r
5317       break;\r
5318 \r
5319     case EP_WhiteMarshall:\r
5320       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5321       fromX = fromY = -1;\r
5322       break;\r
5323 \r
5324     case EP_WhiteKing:\r
5325       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5326       fromX = fromY = -1;\r
5327       break;\r
5328 \r
5329     case EP_BlackPawn:\r
5330       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5331       fromX = fromY = -1;\r
5332       break;\r
5333 \r
5334     case EP_BlackKnight:\r
5335       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5336       fromX = fromY = -1;\r
5337       break;\r
5338 \r
5339     case EP_BlackBishop:\r
5340       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5341       fromX = fromY = -1;\r
5342       break;\r
5343 \r
5344     case EP_BlackRook:\r
5345       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5346       fromX = fromY = -1;\r
5347       break;\r
5348 \r
5349     case EP_BlackQueen:\r
5350       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5351       fromX = fromY = -1;\r
5352       break;\r
5353 \r
5354     case EP_BlackFerz:\r
5355       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5356       fromX = fromY = -1;\r
5357       break;\r
5358 \r
5359     case EP_BlackWazir:\r
5360       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5361       fromX = fromY = -1;\r
5362       break;\r
5363 \r
5364     case EP_BlackAlfil:\r
5365       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5366       fromX = fromY = -1;\r
5367       break;\r
5368 \r
5369     case EP_BlackCannon:\r
5370       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5371       fromX = fromY = -1;\r
5372       break;\r
5373 \r
5374     case EP_BlackCardinal:\r
5375       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5376       fromX = fromY = -1;\r
5377       break;\r
5378 \r
5379     case EP_BlackMarshall:\r
5380       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5381       fromX = fromY = -1;\r
5382       break;\r
5383 \r
5384     case EP_BlackKing:\r
5385       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5386       fromX = fromY = -1;\r
5387       break;\r
5388 \r
5389     case EP_EmptySquare:\r
5390       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5391       fromX = fromY = -1;\r
5392       break;\r
5393 \r
5394     case EP_ClearBoard:\r
5395       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5396       fromX = fromY = -1;\r
5397       break;\r
5398 \r
5399     case EP_White:\r
5400       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5401       fromX = fromY = -1;\r
5402       break;\r
5403 \r
5404     case EP_Black:\r
5405       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5406       fromX = fromY = -1;\r
5407       break;\r
5408 \r
5409     case EP_Promote:\r
5410       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5411       fromX = fromY = -1;\r
5412       break;\r
5413 \r
5414     case EP_Demote:\r
5415       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5416       fromX = fromY = -1;\r
5417       break;\r
5418 \r
5419     case DP_Pawn:\r
5420       DropMenuEvent(WhitePawn, fromX, fromY);\r
5421       fromX = fromY = -1;\r
5422       break;\r
5423 \r
5424     case DP_Knight:\r
5425       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5426       fromX = fromY = -1;\r
5427       break;\r
5428 \r
5429     case DP_Bishop:\r
5430       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5431       fromX = fromY = -1;\r
5432       break;\r
5433 \r
5434     case DP_Rook:\r
5435       DropMenuEvent(WhiteRook, fromX, fromY);\r
5436       fromX = fromY = -1;\r
5437       break;\r
5438 \r
5439     case DP_Queen:\r
5440       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5441       fromX = fromY = -1;\r
5442       break;\r
5443 \r
5444     case IDM_English:\r
5445       barbaric = 0; appData.language = "";\r
5446       TranslateMenus(0);\r
5447       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5448       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5449       lastChecked = wmId;\r
5450       break;\r
5451 \r
5452     default:\r
5453       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5454           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5455       else\r
5456       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5457           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5458           TranslateMenus(0);\r
5459           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5460           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5461           lastChecked = wmId;\r
5462           break;\r
5463       }\r
5464       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5465     }\r
5466     break;\r
5467 \r
5468   case WM_TIMER:\r
5469     switch (wParam) {\r
5470     case CLOCK_TIMER_ID:\r
5471       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5472       clockTimerEvent = 0;\r
5473       DecrementClocks(); /* call into back end */\r
5474       break;\r
5475     case LOAD_GAME_TIMER_ID:\r
5476       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5477       loadGameTimerEvent = 0;\r
5478       AutoPlayGameLoop(); /* call into back end */\r
5479       break;\r
5480     case ANALYSIS_TIMER_ID:\r
5481       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5482                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5483         AnalysisPeriodicEvent(0);\r
5484       } else {\r
5485         KillTimer(hwnd, analysisTimerEvent);\r
5486         analysisTimerEvent = 0;\r
5487       }\r
5488       break;\r
5489     case DELAYED_TIMER_ID:\r
5490       KillTimer(hwnd, delayedTimerEvent);\r
5491       delayedTimerEvent = 0;\r
5492       delayedTimerCallback();\r
5493       break;\r
5494     }\r
5495     break;\r
5496 \r
5497   case WM_USER_Input:\r
5498     InputEvent(hwnd, message, wParam, lParam);\r
5499     break;\r
5500 \r
5501   /* [AS] Also move "attached" child windows */\r
5502   case WM_WINDOWPOSCHANGING:\r
5503 \r
5504     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5505         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5506 \r
5507         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5508             /* Window is moving */\r
5509             RECT rcMain;\r
5510 \r
5511 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5512             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5513             rcMain.right  = wpMain.x + wpMain.width;\r
5514             rcMain.top    = wpMain.y;\r
5515             rcMain.bottom = wpMain.y + wpMain.height;\r
5516             \r
5517             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5518             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5519             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5520             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5521             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5522             wpMain.x = lpwp->x;\r
5523             wpMain.y = lpwp->y;\r
5524         }\r
5525     }\r
5526     break;\r
5527 \r
5528   /* [AS] Snapping */\r
5529   case WM_ENTERSIZEMOVE:\r
5530     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5531     if (hwnd == hwndMain) {\r
5532       doingSizing = TRUE;\r
5533       lastSizing = 0;\r
5534     }\r
5535     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5536     break;\r
5537 \r
5538   case WM_SIZING:\r
5539     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5540     if (hwnd == hwndMain) {\r
5541       lastSizing = wParam;\r
5542     }\r
5543     break;\r
5544 \r
5545   case WM_MOVING:\r
5546     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5547       return OnMoving( &sd, hwnd, wParam, lParam );\r
5548 \r
5549   case WM_EXITSIZEMOVE:\r
5550     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5551     if (hwnd == hwndMain) {\r
5552       RECT client;\r
5553       doingSizing = FALSE;\r
5554       InvalidateRect(hwnd, &boardRect, FALSE);\r
5555       GetClientRect(hwnd, &client);\r
5556       ResizeBoard(client.right, client.bottom, lastSizing);\r
5557       lastSizing = 0;\r
5558       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5559     }\r
5560     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5561     break;\r
5562 \r
5563   case WM_DESTROY: /* message: window being destroyed */\r
5564     PostQuitMessage(0);\r
5565     break;\r
5566 \r
5567   case WM_CLOSE:\r
5568     if (hwnd == hwndMain) {\r
5569       ExitEvent(0);\r
5570     }\r
5571     break;\r
5572 \r
5573   default:      /* Passes it on if unprocessed */\r
5574     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5575   }\r
5576   return 0;\r
5577 }\r
5578 \r
5579 /*---------------------------------------------------------------------------*\\r
5580  *\r
5581  * Misc utility routines\r
5582  *\r
5583 \*---------------------------------------------------------------------------*/\r
5584 \r
5585 /*\r
5586  * Decent random number generator, at least not as bad as Windows\r
5587  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5588  */\r
5589 unsigned int randstate;\r
5590 \r
5591 int\r
5592 myrandom(void)\r
5593 {\r
5594   randstate = randstate * 1664525 + 1013904223;\r
5595   return (int) randstate & 0x7fffffff;\r
5596 }\r
5597 \r
5598 void\r
5599 mysrandom(unsigned int seed)\r
5600 {\r
5601   randstate = seed;\r
5602 }\r
5603 \r
5604 \r
5605 /* \r
5606  * returns TRUE if user selects a different color, FALSE otherwise \r
5607  */\r
5608 \r
5609 BOOL\r
5610 ChangeColor(HWND hwnd, COLORREF *which)\r
5611 {\r
5612   static BOOL firstTime = TRUE;\r
5613   static DWORD customColors[16];\r
5614   CHOOSECOLOR cc;\r
5615   COLORREF newcolor;\r
5616   int i;\r
5617   ColorClass ccl;\r
5618 \r
5619   if (firstTime) {\r
5620     /* Make initial colors in use available as custom colors */\r
5621     /* Should we put the compiled-in defaults here instead? */\r
5622     i = 0;\r
5623     customColors[i++] = lightSquareColor & 0xffffff;\r
5624     customColors[i++] = darkSquareColor & 0xffffff;\r
5625     customColors[i++] = whitePieceColor & 0xffffff;\r
5626     customColors[i++] = blackPieceColor & 0xffffff;\r
5627     customColors[i++] = highlightSquareColor & 0xffffff;\r
5628     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5629 \r
5630     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5631       customColors[i++] = textAttribs[ccl].color;\r
5632     }\r
5633     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5634     firstTime = FALSE;\r
5635   }\r
5636 \r
5637   cc.lStructSize = sizeof(cc);\r
5638   cc.hwndOwner = hwnd;\r
5639   cc.hInstance = NULL;\r
5640   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5641   cc.lpCustColors = (LPDWORD) customColors;\r
5642   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5643 \r
5644   if (!ChooseColor(&cc)) return FALSE;\r
5645 \r
5646   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5647   if (newcolor == *which) return FALSE;\r
5648   *which = newcolor;\r
5649   return TRUE;\r
5650 \r
5651   /*\r
5652   InitDrawingColors();\r
5653   InvalidateRect(hwnd, &boardRect, FALSE);\r
5654   */\r
5655 }\r
5656 \r
5657 BOOLEAN\r
5658 MyLoadSound(MySound *ms)\r
5659 {\r
5660   BOOL ok = FALSE;\r
5661   struct stat st;\r
5662   FILE *f;\r
5663 \r
5664   if (ms->data && ms->flag) free(ms->data);\r
5665   ms->data = NULL;\r
5666 \r
5667   switch (ms->name[0]) {\r
5668   case NULLCHAR:\r
5669     /* Silence */\r
5670     ok = TRUE;\r
5671     break;\r
5672   case '$':\r
5673     /* System sound from Control Panel.  Don't preload here. */\r
5674     ok = TRUE;\r
5675     break;\r
5676   case '!':\r
5677     if (ms->name[1] == NULLCHAR) {\r
5678       /* "!" alone = silence */\r
5679       ok = TRUE;\r
5680     } else {\r
5681       /* Builtin wave resource.  Error if not found. */\r
5682       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5683       if (h == NULL) break;\r
5684       ms->data = (void *)LoadResource(hInst, h);\r
5685       ms->flag = 0; // not maloced, so cannot be freed!\r
5686       if (h == NULL) break;\r
5687       ok = TRUE;\r
5688     }\r
5689     break;\r
5690   default:\r
5691     /* .wav file.  Error if not found. */\r
5692     f = fopen(ms->name, "rb");\r
5693     if (f == NULL) break;\r
5694     if (fstat(fileno(f), &st) < 0) break;\r
5695     ms->data = malloc(st.st_size);\r
5696     ms->flag = 1;\r
5697     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5698     fclose(f);\r
5699     ok = TRUE;\r
5700     break;\r
5701   }\r
5702   if (!ok) {\r
5703     char buf[MSG_SIZ];\r
5704       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5705     DisplayError(buf, GetLastError());\r
5706   }\r
5707   return ok;\r
5708 }\r
5709 \r
5710 BOOLEAN\r
5711 MyPlaySound(MySound *ms)\r
5712 {\r
5713   BOOLEAN ok = FALSE;\r
5714 \r
5715   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5716   switch (ms->name[0]) {\r
5717   case NULLCHAR:\r
5718         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5719     /* Silence */\r
5720     ok = TRUE;\r
5721     break;\r
5722   case '$':\r
5723     /* System sound from Control Panel (deprecated feature).\r
5724        "$" alone or an unset sound name gets default beep (still in use). */\r
5725     if (ms->name[1]) {\r
5726       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5727     }\r
5728     if (!ok) ok = MessageBeep(MB_OK);\r
5729     break; \r
5730   case '!':\r
5731     /* Builtin wave resource, or "!" alone for silence */\r
5732     if (ms->name[1]) {\r
5733       if (ms->data == NULL) return FALSE;\r
5734       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5735     } else {\r
5736       ok = TRUE;\r
5737     }\r
5738     break;\r
5739   default:\r
5740     /* .wav file.  Error if not found. */\r
5741     if (ms->data == NULL) return FALSE;\r
5742     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5743     break;\r
5744   }\r
5745   /* Don't print an error: this can happen innocently if the sound driver\r
5746      is busy; for instance, if another instance of WinBoard is playing\r
5747      a sound at about the same time. */\r
5748   return ok;\r
5749 }\r
5750 \r
5751 \r
5752 LRESULT CALLBACK\r
5753 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5754 {\r
5755   BOOL ok;\r
5756   OPENFILENAME *ofn;\r
5757   static UINT *number; /* gross that this is static */\r
5758 \r
5759   switch (message) {\r
5760   case WM_INITDIALOG: /* message: initialize dialog box */\r
5761     /* Center the dialog over the application window */\r
5762     ofn = (OPENFILENAME *) lParam;\r
5763     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5764       number = (UINT *) ofn->lCustData;\r
5765       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5766     } else {\r
5767       number = NULL;\r
5768     }\r
5769     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5770     Translate(hDlg, 1536);\r
5771     return FALSE;  /* Allow for further processing */\r
5772 \r
5773   case WM_COMMAND:\r
5774     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5775       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5776     }\r
5777     return FALSE;  /* Allow for further processing */\r
5778   }\r
5779   return FALSE;\r
5780 }\r
5781 \r
5782 UINT APIENTRY\r
5783 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5784 {\r
5785   static UINT *number;\r
5786   OPENFILENAME *ofname;\r
5787   OFNOTIFY *ofnot;\r
5788   switch (uiMsg) {\r
5789   case WM_INITDIALOG:\r
5790     Translate(hdlg, DLG_IndexNumber);\r
5791     ofname = (OPENFILENAME *)lParam;\r
5792     number = (UINT *)(ofname->lCustData);\r
5793     break;\r
5794   case WM_NOTIFY:\r
5795     ofnot = (OFNOTIFY *)lParam;\r
5796     if (ofnot->hdr.code == CDN_FILEOK) {\r
5797       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5798     }\r
5799     break;\r
5800   }\r
5801   return 0;\r
5802 }\r
5803 \r
5804 \r
5805 FILE *\r
5806 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5807                char *nameFilt, char *dlgTitle, UINT *number,\r
5808                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5809 {\r
5810   OPENFILENAME openFileName;\r
5811   char buf1[MSG_SIZ];\r
5812   FILE *f;\r
5813 \r
5814   if (fileName == NULL) fileName = buf1;\r
5815   if (defName == NULL) {\r
5816     safeStrCpy(fileName, "*.", 3 );\r
5817     strcat(fileName, defExt);\r
5818   } else {\r
5819     safeStrCpy(fileName, defName, MSG_SIZ );\r
5820   }\r
5821     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5822   if (number) *number = 0;\r
5823 \r
5824   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5825   openFileName.hwndOwner         = hwnd;\r
5826   openFileName.hInstance         = (HANDLE) hInst;\r
5827   openFileName.lpstrFilter       = nameFilt;\r
5828   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5829   openFileName.nMaxCustFilter    = 0L;\r
5830   openFileName.nFilterIndex      = 1L;\r
5831   openFileName.lpstrFile         = fileName;\r
5832   openFileName.nMaxFile          = MSG_SIZ;\r
5833   openFileName.lpstrFileTitle    = fileTitle;\r
5834   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5835   openFileName.lpstrInitialDir   = NULL;\r
5836   openFileName.lpstrTitle        = dlgTitle;\r
5837   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5838     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5839     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5840     | (oldDialog ? 0 : OFN_EXPLORER);\r
5841   openFileName.nFileOffset       = 0;\r
5842   openFileName.nFileExtension    = 0;\r
5843   openFileName.lpstrDefExt       = defExt;\r
5844   openFileName.lCustData         = (LONG) number;\r
5845   openFileName.lpfnHook          = oldDialog ?\r
5846     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5847   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5848 \r
5849   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5850                         GetOpenFileName(&openFileName)) {\r
5851     /* open the file */\r
5852     f = fopen(openFileName.lpstrFile, write);\r
5853     if (f == NULL) {\r
5854       MessageBox(hwnd, _("File open failed"), NULL,\r
5855                  MB_OK|MB_ICONEXCLAMATION);\r
5856       return NULL;\r
5857     }\r
5858   } else {\r
5859     int err = CommDlgExtendedError();\r
5860     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5861     return FALSE;\r
5862   }\r
5863   return f;\r
5864 }\r
5865 \r
5866 \r
5867 \r
5868 VOID APIENTRY\r
5869 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5870 {\r
5871   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5872 \r
5873   /*\r
5874    * Get the first pop-up menu in the menu template. This is the\r
5875    * menu that TrackPopupMenu displays.\r
5876    */\r
5877   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5878   TranslateOneMenu(10, hmenuTrackPopup);\r
5879 \r
5880   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5881 \r
5882   /*\r
5883    * TrackPopup uses screen coordinates, so convert the\r
5884    * coordinates of the mouse click to screen coordinates.\r
5885    */\r
5886   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5887 \r
5888   /* Draw and track the floating pop-up menu. */\r
5889   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5890                  pt.x, pt.y, 0, hwnd, NULL);\r
5891 \r
5892   /* Destroy the menu.*/\r
5893   DestroyMenu(hmenu);\r
5894 }\r
5895    \r
5896 typedef struct {\r
5897   HWND hDlg, hText;\r
5898   int sizeX, sizeY, newSizeX, newSizeY;\r
5899   HDWP hdwp;\r
5900 } ResizeEditPlusButtonsClosure;\r
5901 \r
5902 BOOL CALLBACK\r
5903 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5904 {\r
5905   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5906   RECT rect;\r
5907   POINT pt;\r
5908 \r
5909   if (hChild == cl->hText) return TRUE;\r
5910   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5911   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5912   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5913   ScreenToClient(cl->hDlg, &pt);\r
5914   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5915     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5916   return TRUE;\r
5917 }\r
5918 \r
5919 /* Resize a dialog that has a (rich) edit field filling most of\r
5920    the top, with a row of buttons below */\r
5921 VOID\r
5922 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5923 {\r
5924   RECT rectText;\r
5925   int newTextHeight, newTextWidth;\r
5926   ResizeEditPlusButtonsClosure cl;\r
5927   \r
5928   /*if (IsIconic(hDlg)) return;*/\r
5929   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5930   \r
5931   cl.hdwp = BeginDeferWindowPos(8);\r
5932 \r
5933   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5934   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5935   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5936   if (newTextHeight < 0) {\r
5937     newSizeY += -newTextHeight;\r
5938     newTextHeight = 0;\r
5939   }\r
5940   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5941     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5942 \r
5943   cl.hDlg = hDlg;\r
5944   cl.hText = hText;\r
5945   cl.sizeX = sizeX;\r
5946   cl.sizeY = sizeY;\r
5947   cl.newSizeX = newSizeX;\r
5948   cl.newSizeY = newSizeY;\r
5949   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5950 \r
5951   EndDeferWindowPos(cl.hdwp);\r
5952 }\r
5953 \r
5954 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5955 {\r
5956     RECT    rChild, rParent;\r
5957     int     wChild, hChild, wParent, hParent;\r
5958     int     wScreen, hScreen, xNew, yNew;\r
5959     HDC     hdc;\r
5960 \r
5961     /* Get the Height and Width of the child window */\r
5962     GetWindowRect (hwndChild, &rChild);\r
5963     wChild = rChild.right - rChild.left;\r
5964     hChild = rChild.bottom - rChild.top;\r
5965 \r
5966     /* Get the Height and Width of the parent window */\r
5967     GetWindowRect (hwndParent, &rParent);\r
5968     wParent = rParent.right - rParent.left;\r
5969     hParent = rParent.bottom - rParent.top;\r
5970 \r
5971     /* Get the display limits */\r
5972     hdc = GetDC (hwndChild);\r
5973     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5974     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5975     ReleaseDC(hwndChild, hdc);\r
5976 \r
5977     /* Calculate new X position, then adjust for screen */\r
5978     xNew = rParent.left + ((wParent - wChild) /2);\r
5979     if (xNew < 0) {\r
5980         xNew = 0;\r
5981     } else if ((xNew+wChild) > wScreen) {\r
5982         xNew = wScreen - wChild;\r
5983     }\r
5984 \r
5985     /* Calculate new Y position, then adjust for screen */\r
5986     if( mode == 0 ) {\r
5987         yNew = rParent.top  + ((hParent - hChild) /2);\r
5988     }\r
5989     else {\r
5990         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5991     }\r
5992 \r
5993     if (yNew < 0) {\r
5994         yNew = 0;\r
5995     } else if ((yNew+hChild) > hScreen) {\r
5996         yNew = hScreen - hChild;\r
5997     }\r
5998 \r
5999     /* Set it, and return */\r
6000     return SetWindowPos (hwndChild, NULL,\r
6001                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6002 }\r
6003 \r
6004 /* Center one window over another */\r
6005 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6006 {\r
6007     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6008 }\r
6009 \r
6010 /*---------------------------------------------------------------------------*\\r
6011  *\r
6012  * Startup Dialog functions\r
6013  *\r
6014 \*---------------------------------------------------------------------------*/\r
6015 void\r
6016 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6017 {\r
6018   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6019 \r
6020   while (*cd != NULL) {\r
6021     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6022     cd++;\r
6023   }\r
6024 }\r
6025 \r
6026 void\r
6027 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6028 {\r
6029   char buf1[MAX_ARG_LEN];\r
6030   int len;\r
6031 \r
6032   if (str[0] == '@') {\r
6033     FILE* f = fopen(str + 1, "r");\r
6034     if (f == NULL) {\r
6035       DisplayFatalError(str + 1, errno, 2);\r
6036       return;\r
6037     }\r
6038     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6039     fclose(f);\r
6040     buf1[len] = NULLCHAR;\r
6041     str = buf1;\r
6042   }\r
6043 \r
6044   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6045 \r
6046   for (;;) {\r
6047     char buf[MSG_SIZ];\r
6048     char *end = strchr(str, '\n');\r
6049     if (end == NULL) return;\r
6050     memcpy(buf, str, end - str);\r
6051     buf[end - str] = NULLCHAR;\r
6052     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6053     str = end + 1;\r
6054   }\r
6055 }\r
6056 \r
6057 void\r
6058 SetStartupDialogEnables(HWND hDlg)\r
6059 {\r
6060   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6061     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6062     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6063   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6064     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6065   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6066     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6067   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6068     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6069   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6070     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6071     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6072     IsDlgButtonChecked(hDlg, OPT_View));\r
6073 }\r
6074 \r
6075 char *\r
6076 QuoteForFilename(char *filename)\r
6077 {\r
6078   int dquote, space;\r
6079   dquote = strchr(filename, '"') != NULL;\r
6080   space = strchr(filename, ' ') != NULL;\r
6081   if (dquote || space) {\r
6082     if (dquote) {\r
6083       return "'";\r
6084     } else {\r
6085       return "\"";\r
6086     }\r
6087   } else {\r
6088     return "";\r
6089   }\r
6090 }\r
6091 \r
6092 VOID\r
6093 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6094 {\r
6095   char buf[MSG_SIZ];\r
6096   char *q;\r
6097 \r
6098   InitComboStringsFromOption(hwndCombo, nthnames);\r
6099   q = QuoteForFilename(nthcp);\r
6100     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6101   if (*nthdir != NULLCHAR) {\r
6102     q = QuoteForFilename(nthdir);\r
6103       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6104   }\r
6105   if (*nthcp == NULLCHAR) {\r
6106     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6107   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6108     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6109     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6110   }\r
6111 }\r
6112 \r
6113 LRESULT CALLBACK\r
6114 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6115 {\r
6116   char buf[MSG_SIZ];\r
6117   HANDLE hwndCombo;\r
6118   char *p;\r
6119 \r
6120   switch (message) {\r
6121   case WM_INITDIALOG:\r
6122     /* Center the dialog */\r
6123     CenterWindow (hDlg, GetDesktopWindow());\r
6124     Translate(hDlg, DLG_Startup);\r
6125     /* Initialize the dialog items */\r
6126     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6127                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6128                   firstChessProgramNames);\r
6129     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6130                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6131                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6132     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6133     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6134       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6135     if (*appData.icsHelper != NULLCHAR) {\r
6136       char *q = QuoteForFilename(appData.icsHelper);\r
6137       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6138     }\r
6139     if (*appData.icsHost == NULLCHAR) {\r
6140       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6141       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6142     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6143       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6144       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6145     }\r
6146 \r
6147     if (appData.icsActive) {\r
6148       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6149     }\r
6150     else if (appData.noChessProgram) {\r
6151       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6152     }\r
6153     else {\r
6154       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6155     }\r
6156 \r
6157     SetStartupDialogEnables(hDlg);\r
6158     return TRUE;\r
6159 \r
6160   case WM_COMMAND:\r
6161     switch (LOWORD(wParam)) {\r
6162     case IDOK:\r
6163       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6164         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6165         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6166         p = buf;\r
6167         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6168         ParseArgs(StringGet, &p);\r
6169         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6170         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6171         p = buf;\r
6172         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6173         ParseArgs(StringGet, &p);\r
6174         SwapEngines(singleList); // ... and then make it 'second'\r
6175         appData.noChessProgram = FALSE;\r
6176         appData.icsActive = FALSE;\r
6177       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6178         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6179         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6180         p = buf;\r
6181         ParseArgs(StringGet, &p);\r
6182         if (appData.zippyPlay) {\r
6183           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6184           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6185           p = buf;\r
6186           ParseArgs(StringGet, &p);\r
6187         }\r
6188       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6189         appData.noChessProgram = TRUE;\r
6190         appData.icsActive = FALSE;\r
6191       } else {\r
6192         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6193                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6194         return TRUE;\r
6195       }\r
6196       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6197         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6198         p = buf;\r
6199         ParseArgs(StringGet, &p);\r
6200       }\r
6201       EndDialog(hDlg, TRUE);\r
6202       return TRUE;\r
6203 \r
6204     case IDCANCEL:\r
6205       ExitEvent(0);\r
6206       return TRUE;\r
6207 \r
6208     case IDM_HELPCONTENTS:\r
6209       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6210         MessageBox (GetFocus(),\r
6211                     _("Unable to activate help"),\r
6212                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6213       }\r
6214       break;\r
6215 \r
6216     default:\r
6217       SetStartupDialogEnables(hDlg);\r
6218       break;\r
6219     }\r
6220     break;\r
6221   }\r
6222   return FALSE;\r
6223 }\r
6224 \r
6225 /*---------------------------------------------------------------------------*\\r
6226  *\r
6227  * About box dialog functions\r
6228  *\r
6229 \*---------------------------------------------------------------------------*/\r
6230 \r
6231 /* Process messages for "About" dialog box */\r
6232 LRESULT CALLBACK\r
6233 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6234 {\r
6235   switch (message) {\r
6236   case WM_INITDIALOG: /* message: initialize dialog box */\r
6237     /* Center the dialog over the application window */\r
6238     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6239     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6240     Translate(hDlg, ABOUTBOX);\r
6241     JAWS_COPYRIGHT\r
6242     return (TRUE);\r
6243 \r
6244   case WM_COMMAND: /* message: received a command */\r
6245     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6246         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6247       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6248       return (TRUE);\r
6249     }\r
6250     break;\r
6251   }\r
6252   return (FALSE);\r
6253 }\r
6254 \r
6255 /*---------------------------------------------------------------------------*\\r
6256  *\r
6257  * Comment Dialog functions\r
6258  *\r
6259 \*---------------------------------------------------------------------------*/\r
6260 \r
6261 LRESULT CALLBACK\r
6262 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6263 {\r
6264   static HANDLE hwndText = NULL;\r
6265   int len, newSizeX, newSizeY, flags;\r
6266   static int sizeX, sizeY;\r
6267   char *str;\r
6268   RECT rect;\r
6269   MINMAXINFO *mmi;\r
6270 \r
6271   switch (message) {\r
6272   case WM_INITDIALOG: /* message: initialize dialog box */\r
6273     /* Initialize the dialog items */\r
6274     Translate(hDlg, DLG_EditComment);\r
6275     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6276     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6277     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6278     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6279     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6280     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6281     SetWindowText(hDlg, commentTitle);\r
6282     if (editComment) {\r
6283       SetFocus(hwndText);\r
6284     } else {\r
6285       SetFocus(GetDlgItem(hDlg, IDOK));\r
6286     }\r
6287     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6288                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6289                 MAKELPARAM(FALSE, 0));\r
6290     /* Size and position the dialog */\r
6291     if (!commentDialog) {\r
6292       commentDialog = hDlg;\r
6293       flags = SWP_NOZORDER;\r
6294       GetClientRect(hDlg, &rect);\r
6295       sizeX = rect.right;\r
6296       sizeY = rect.bottom;\r
6297       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6298           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6299         WINDOWPLACEMENT wp;\r
6300         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6301         wp.length = sizeof(WINDOWPLACEMENT);\r
6302         wp.flags = 0;\r
6303         wp.showCmd = SW_SHOW;\r
6304         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6305         wp.rcNormalPosition.left = wpComment.x;\r
6306         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6307         wp.rcNormalPosition.top = wpComment.y;\r
6308         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6309         SetWindowPlacement(hDlg, &wp);\r
6310 \r
6311         GetClientRect(hDlg, &rect);\r
6312         newSizeX = rect.right;\r
6313         newSizeY = rect.bottom;\r
6314         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6315                               newSizeX, newSizeY);\r
6316         sizeX = newSizeX;\r
6317         sizeY = newSizeY;\r
6318       }\r
6319     }\r
6320     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6321     return FALSE;\r
6322 \r
6323   case WM_COMMAND: /* message: received a command */\r
6324     switch (LOWORD(wParam)) {\r
6325     case IDOK:\r
6326       if (editComment) {\r
6327         char *p, *q;\r
6328         /* Read changed options from the dialog box */\r
6329         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6330         len = GetWindowTextLength(hwndText);\r
6331         str = (char *) malloc(len + 1);\r
6332         GetWindowText(hwndText, str, len + 1);\r
6333         p = q = str;\r
6334         while (*q) {\r
6335           if (*q == '\r')\r
6336             q++;\r
6337           else\r
6338             *p++ = *q++;\r
6339         }\r
6340         *p = NULLCHAR;\r
6341         ReplaceComment(commentIndex, str);\r
6342         free(str);\r
6343       }\r
6344       CommentPopDown();\r
6345       return TRUE;\r
6346 \r
6347     case IDCANCEL:\r
6348     case OPT_CancelComment:\r
6349       CommentPopDown();\r
6350       return TRUE;\r
6351 \r
6352     case OPT_ClearComment:\r
6353       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6354       break;\r
6355 \r
6356     case OPT_EditComment:\r
6357       EditCommentEvent();\r
6358       return TRUE;\r
6359 \r
6360     default:\r
6361       break;\r
6362     }\r
6363     break;\r
6364 \r
6365   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6366         if( wParam == OPT_CommentText ) {\r
6367             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6368 \r
6369             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6370                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6371                 POINTL pt;\r
6372                 LRESULT index;\r
6373 \r
6374                 pt.x = LOWORD( lpMF->lParam );\r
6375                 pt.y = HIWORD( lpMF->lParam );\r
6376 \r
6377                 if(lpMF->msg == WM_CHAR) {\r
6378                         CHARRANGE sel;\r
6379                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6380                         index = sel.cpMin;\r
6381                 } else\r
6382                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6383 \r
6384                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6385                 len = GetWindowTextLength(hwndText);\r
6386                 str = (char *) malloc(len + 1);\r
6387                 GetWindowText(hwndText, str, len + 1);\r
6388                 ReplaceComment(commentIndex, str);\r
6389                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6390                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6391                 free(str);\r
6392 \r
6393                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6394                 lpMF->msg = WM_USER;\r
6395 \r
6396                 return TRUE;\r
6397             }\r
6398         }\r
6399         break;\r
6400 \r
6401   case WM_SIZE:\r
6402     newSizeX = LOWORD(lParam);\r
6403     newSizeY = HIWORD(lParam);\r
6404     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6405     sizeX = newSizeX;\r
6406     sizeY = newSizeY;\r
6407     break;\r
6408 \r
6409   case WM_GETMINMAXINFO:\r
6410     /* Prevent resizing window too small */\r
6411     mmi = (MINMAXINFO *) lParam;\r
6412     mmi->ptMinTrackSize.x = 100;\r
6413     mmi->ptMinTrackSize.y = 100;\r
6414     break;\r
6415   }\r
6416   return FALSE;\r
6417 }\r
6418 \r
6419 VOID\r
6420 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6421 {\r
6422   FARPROC lpProc;\r
6423   char *p, *q;\r
6424 \r
6425   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6426 \r
6427   if (str == NULL) str = "";\r
6428   p = (char *) malloc(2 * strlen(str) + 2);\r
6429   q = p;\r
6430   while (*str) {\r
6431     if (*str == '\n') *q++ = '\r';\r
6432     *q++ = *str++;\r
6433   }\r
6434   *q = NULLCHAR;\r
6435   if (commentText != NULL) free(commentText);\r
6436 \r
6437   commentIndex = index;\r
6438   commentTitle = title;\r
6439   commentText = p;\r
6440   editComment = edit;\r
6441 \r
6442   if (commentDialog) {\r
6443     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6444     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6445   } else {\r
6446     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6447     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6448                  hwndMain, (DLGPROC)lpProc);\r
6449     FreeProcInstance(lpProc);\r
6450   }\r
6451   commentUp = TRUE;\r
6452 }\r
6453 \r
6454 \r
6455 /*---------------------------------------------------------------------------*\\r
6456  *\r
6457  * Type-in move dialog functions\r
6458  * \r
6459 \*---------------------------------------------------------------------------*/\r
6460 \r
6461 LRESULT CALLBACK\r
6462 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6463 {\r
6464   char move[MSG_SIZ];\r
6465   HWND hInput;\r
6466 \r
6467   switch (message) {\r
6468   case WM_INITDIALOG:\r
6469     move[0] = (char) lParam;\r
6470     move[1] = NULLCHAR;\r
6471     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6472     Translate(hDlg, DLG_TypeInMove);\r
6473     hInput = GetDlgItem(hDlg, OPT_Move);\r
6474     SetWindowText(hInput, move);\r
6475     SetFocus(hInput);\r
6476     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6477     return FALSE;\r
6478 \r
6479   case WM_COMMAND:\r
6480     switch (LOWORD(wParam)) {\r
6481     case IDOK:\r
6482 \r
6483       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6484       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6485       TypeInDoneEvent(move);\r
6486       EndDialog(hDlg, TRUE);\r
6487       return TRUE;\r
6488     case IDCANCEL:\r
6489       EndDialog(hDlg, FALSE);\r
6490       return TRUE;\r
6491     default:\r
6492       break;\r
6493     }\r
6494     break;\r
6495   }\r
6496   return FALSE;\r
6497 }\r
6498 \r
6499 VOID\r
6500 PopUpMoveDialog(char firstchar)\r
6501 {\r
6502     FARPROC lpProc;\r
6503 \r
6504       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6505       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6506         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6507       FreeProcInstance(lpProc);\r
6508 }\r
6509 \r
6510 /*---------------------------------------------------------------------------*\\r
6511  *\r
6512  * Type-in name dialog functions\r
6513  * \r
6514 \*---------------------------------------------------------------------------*/\r
6515 \r
6516 LRESULT CALLBACK\r
6517 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6518 {\r
6519   char move[MSG_SIZ];\r
6520   HWND hInput;\r
6521 \r
6522   switch (message) {\r
6523   case WM_INITDIALOG:\r
6524     move[0] = (char) lParam;\r
6525     move[1] = NULLCHAR;\r
6526     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6527     Translate(hDlg, DLG_TypeInName);\r
6528     hInput = GetDlgItem(hDlg, OPT_Name);\r
6529     SetWindowText(hInput, move);\r
6530     SetFocus(hInput);\r
6531     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6532     return FALSE;\r
6533 \r
6534   case WM_COMMAND:\r
6535     switch (LOWORD(wParam)) {\r
6536     case IDOK:\r
6537       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6538       appData.userName = strdup(move);\r
6539       SetUserLogo();\r
6540       SetGameInfo();\r
6541       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6542         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6543         DisplayTitle(move);\r
6544       }\r
6545 \r
6546 \r
6547       EndDialog(hDlg, TRUE);\r
6548       return TRUE;\r
6549     case IDCANCEL:\r
6550       EndDialog(hDlg, FALSE);\r
6551       return TRUE;\r
6552     default:\r
6553       break;\r
6554     }\r
6555     break;\r
6556   }\r
6557   return FALSE;\r
6558 }\r
6559 \r
6560 VOID\r
6561 PopUpNameDialog(char firstchar)\r
6562 {\r
6563     FARPROC lpProc;\r
6564     \r
6565       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6566       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6567         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6568       FreeProcInstance(lpProc);\r
6569 }\r
6570 \r
6571 /*---------------------------------------------------------------------------*\\r
6572  *\r
6573  *  Error dialogs\r
6574  * \r
6575 \*---------------------------------------------------------------------------*/\r
6576 \r
6577 /* Nonmodal error box */\r
6578 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6579                              WPARAM wParam, LPARAM lParam);\r
6580 \r
6581 VOID\r
6582 ErrorPopUp(char *title, char *content)\r
6583 {\r
6584   FARPROC lpProc;\r
6585   char *p, *q;\r
6586   BOOLEAN modal = hwndMain == NULL;\r
6587 \r
6588   p = content;\r
6589   q = errorMessage;\r
6590   while (*p) {\r
6591     if (*p == '\n') {\r
6592       if (modal) {\r
6593         *q++ = ' ';\r
6594         p++;\r
6595       } else {\r
6596         *q++ = '\r';\r
6597         *q++ = *p++;\r
6598       }\r
6599     } else {\r
6600       *q++ = *p++;\r
6601     }\r
6602   }\r
6603   *q = NULLCHAR;\r
6604   strncpy(errorTitle, title, sizeof(errorTitle));\r
6605   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6606   \r
6607   if (modal) {\r
6608     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6609   } else {\r
6610     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6611     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6612                  hwndMain, (DLGPROC)lpProc);\r
6613     FreeProcInstance(lpProc);\r
6614   }\r
6615 }\r
6616 \r
6617 VOID\r
6618 ErrorPopDown()\r
6619 {\r
6620   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6621   if (errorDialog == NULL) return;\r
6622   DestroyWindow(errorDialog);\r
6623   errorDialog = NULL;\r
6624   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6625 }\r
6626 \r
6627 LRESULT CALLBACK\r
6628 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6629 {\r
6630   HANDLE hwndText;\r
6631   RECT rChild;\r
6632 \r
6633   switch (message) {\r
6634   case WM_INITDIALOG:\r
6635     GetWindowRect(hDlg, &rChild);\r
6636 \r
6637     /*\r
6638     SetWindowPos(hDlg, NULL, rChild.left,\r
6639       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6640       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6641     */\r
6642 \r
6643     /* \r
6644         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6645         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6646         and it doesn't work when you resize the dialog.\r
6647         For now, just give it a default position.\r
6648     */\r
6649     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6650     Translate(hDlg, DLG_Error);\r
6651 \r
6652     errorDialog = hDlg;\r
6653     SetWindowText(hDlg, errorTitle);\r
6654     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6655     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6656     return FALSE;\r
6657 \r
6658   case WM_COMMAND:\r
6659     switch (LOWORD(wParam)) {\r
6660     case IDOK:\r
6661     case IDCANCEL:\r
6662       if (errorDialog == hDlg) errorDialog = NULL;\r
6663       DestroyWindow(hDlg);\r
6664       return TRUE;\r
6665 \r
6666     default:\r
6667       break;\r
6668     }\r
6669     break;\r
6670   }\r
6671   return FALSE;\r
6672 }\r
6673 \r
6674 #ifdef GOTHIC\r
6675 HWND gothicDialog = NULL;\r
6676 \r
6677 LRESULT CALLBACK\r
6678 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6679 {\r
6680   HANDLE hwndText;\r
6681   RECT rChild;\r
6682   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6683 \r
6684   switch (message) {\r
6685   case WM_INITDIALOG:\r
6686     GetWindowRect(hDlg, &rChild);\r
6687 \r
6688     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6689                                                              SWP_NOZORDER);\r
6690 \r
6691     /* \r
6692         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6693         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6694         and it doesn't work when you resize the dialog.\r
6695         For now, just give it a default position.\r
6696     */\r
6697     gothicDialog = hDlg;\r
6698     SetWindowText(hDlg, errorTitle);\r
6699     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6700     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6701     return FALSE;\r
6702 \r
6703   case WM_COMMAND:\r
6704     switch (LOWORD(wParam)) {\r
6705     case IDOK:\r
6706     case IDCANCEL:\r
6707       if (errorDialog == hDlg) errorDialog = NULL;\r
6708       DestroyWindow(hDlg);\r
6709       return TRUE;\r
6710 \r
6711     default:\r
6712       break;\r
6713     }\r
6714     break;\r
6715   }\r
6716   return FALSE;\r
6717 }\r
6718 \r
6719 VOID\r
6720 GothicPopUp(char *title, VariantClass variant)\r
6721 {\r
6722   FARPROC lpProc;\r
6723   static char *lastTitle;\r
6724 \r
6725   strncpy(errorTitle, title, sizeof(errorTitle));\r
6726   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6727 \r
6728   if(lastTitle != title && gothicDialog != NULL) {\r
6729     DestroyWindow(gothicDialog);\r
6730     gothicDialog = NULL;\r
6731   }\r
6732   if(variant != VariantNormal && gothicDialog == NULL) {\r
6733     title = lastTitle;\r
6734     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6735     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6736                  hwndMain, (DLGPROC)lpProc);\r
6737     FreeProcInstance(lpProc);\r
6738   }\r
6739 }\r
6740 #endif\r
6741 \r
6742 /*---------------------------------------------------------------------------*\\r
6743  *\r
6744  *  Ics Interaction console functions\r
6745  *\r
6746 \*---------------------------------------------------------------------------*/\r
6747 \r
6748 #define HISTORY_SIZE 64\r
6749 static char *history[HISTORY_SIZE];\r
6750 int histIn = 0, histP = 0;\r
6751 \r
6752 VOID\r
6753 SaveInHistory(char *cmd)\r
6754 {\r
6755   if (history[histIn] != NULL) {\r
6756     free(history[histIn]);\r
6757     history[histIn] = NULL;\r
6758   }\r
6759   if (*cmd == NULLCHAR) return;\r
6760   history[histIn] = StrSave(cmd);\r
6761   histIn = (histIn + 1) % HISTORY_SIZE;\r
6762   if (history[histIn] != NULL) {\r
6763     free(history[histIn]);\r
6764     history[histIn] = NULL;\r
6765   }\r
6766   histP = histIn;\r
6767 }\r
6768 \r
6769 char *\r
6770 PrevInHistory(char *cmd)\r
6771 {\r
6772   int newhp;\r
6773   if (histP == histIn) {\r
6774     if (history[histIn] != NULL) free(history[histIn]);\r
6775     history[histIn] = StrSave(cmd);\r
6776   }\r
6777   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6778   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6779   histP = newhp;\r
6780   return history[histP];\r
6781 }\r
6782 \r
6783 char *\r
6784 NextInHistory()\r
6785 {\r
6786   if (histP == histIn) return NULL;\r
6787   histP = (histP + 1) % HISTORY_SIZE;\r
6788   return history[histP];   \r
6789 }\r
6790 \r
6791 HMENU\r
6792 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6793 {\r
6794   HMENU hmenu, h;\r
6795   int i = 0;\r
6796   hmenu = LoadMenu(hInst, "TextMenu");\r
6797   h = GetSubMenu(hmenu, 0);\r
6798   while (e->item) {\r
6799     if (strcmp(e->item, "-") == 0) {\r
6800       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6801     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6802       int flags = MF_STRING, j = 0;\r
6803       if (e->item[0] == '|') {\r
6804         flags |= MF_MENUBARBREAK;\r
6805         j++;\r
6806       }\r
6807       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6808       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6809     }\r
6810     e++;\r
6811     i++;\r
6812   } \r
6813   return hmenu;\r
6814 }\r
6815 \r
6816 WNDPROC consoleTextWindowProc;\r
6817 \r
6818 void\r
6819 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6820 {\r
6821   char buf[MSG_SIZ], name[MSG_SIZ];\r
6822   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6823   CHARRANGE sel;\r
6824 \r
6825   if (!getname) {\r
6826     SetWindowText(hInput, command);\r
6827     if (immediate) {\r
6828       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6829     } else {\r
6830       sel.cpMin = 999999;\r
6831       sel.cpMax = 999999;\r
6832       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6833       SetFocus(hInput);\r
6834     }\r
6835     return;\r
6836   }    \r
6837   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6838   if (sel.cpMin == sel.cpMax) {\r
6839     /* Expand to surrounding word */\r
6840     TEXTRANGE tr;\r
6841     do {\r
6842       tr.chrg.cpMax = sel.cpMin;\r
6843       tr.chrg.cpMin = --sel.cpMin;\r
6844       if (sel.cpMin < 0) break;\r
6845       tr.lpstrText = name;\r
6846       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6847     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6848     sel.cpMin++;\r
6849 \r
6850     do {\r
6851       tr.chrg.cpMin = sel.cpMax;\r
6852       tr.chrg.cpMax = ++sel.cpMax;\r
6853       tr.lpstrText = name;\r
6854       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6855     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6856     sel.cpMax--;\r
6857 \r
6858     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6859       MessageBeep(MB_ICONEXCLAMATION);\r
6860       return;\r
6861     }\r
6862     tr.chrg = sel;\r
6863     tr.lpstrText = name;\r
6864     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6865   } else {\r
6866     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6867       MessageBeep(MB_ICONEXCLAMATION);\r
6868       return;\r
6869     }\r
6870     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6871   }\r
6872   if (immediate) {\r
6873     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6874     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6875     SetWindowText(hInput, buf);\r
6876     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6877   } else {\r
6878     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6879       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6880     SetWindowText(hInput, buf);\r
6881     sel.cpMin = 999999;\r
6882     sel.cpMax = 999999;\r
6883     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6884     SetFocus(hInput);\r
6885   }\r
6886 }\r
6887 \r
6888 LRESULT CALLBACK \r
6889 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6890 {\r
6891   HWND hInput;\r
6892   CHARRANGE sel;\r
6893 \r
6894   switch (message) {\r
6895   case WM_KEYDOWN:\r
6896     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6897     if(wParam=='R') return 0;\r
6898     switch (wParam) {\r
6899     case VK_PRIOR:\r
6900       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6901       return 0;\r
6902     case VK_NEXT:\r
6903       sel.cpMin = 999999;\r
6904       sel.cpMax = 999999;\r
6905       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6906       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6907       return 0;\r
6908     }\r
6909     break;\r
6910   case WM_CHAR:\r
6911    if(wParam != '\022') {\r
6912     if (wParam == '\t') {\r
6913       if (GetKeyState(VK_SHIFT) < 0) {\r
6914         /* shifted */\r
6915         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6916         if (buttonDesc[0].hwnd) {\r
6917           SetFocus(buttonDesc[0].hwnd);\r
6918         } else {\r
6919           SetFocus(hwndMain);\r
6920         }\r
6921       } else {\r
6922         /* unshifted */\r
6923         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6924       }\r
6925     } else {\r
6926       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6927       JAWS_DELETE( SetFocus(hInput); )\r
6928       SendMessage(hInput, message, wParam, lParam);\r
6929     }\r
6930     return 0;\r
6931    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6932    lParam = -1;\r
6933   case WM_RBUTTONDOWN:\r
6934     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6935       /* Move selection here if it was empty */\r
6936       POINT pt;\r
6937       pt.x = LOWORD(lParam);\r
6938       pt.y = HIWORD(lParam);\r
6939       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6940       if (sel.cpMin == sel.cpMax) {\r
6941         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6942         sel.cpMax = sel.cpMin;\r
6943         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6944       }\r
6945       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6946 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6947       POINT pt;\r
6948       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6949       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6950       if (sel.cpMin == sel.cpMax) {\r
6951         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6952         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6953       }\r
6954       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6955         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6956       }\r
6957       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6958       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6959       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6960       MenuPopup(hwnd, pt, hmenu, -1);\r
6961 }\r
6962     }\r
6963     return 0;\r
6964   case WM_RBUTTONUP:\r
6965     if (GetKeyState(VK_SHIFT) & ~1) {\r
6966       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6967         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6968     }\r
6969     return 0;\r
6970   case WM_PASTE:\r
6971     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6972     SetFocus(hInput);\r
6973     return SendMessage(hInput, message, wParam, lParam);\r
6974   case WM_MBUTTONDOWN:\r
6975     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6976   case WM_COMMAND:\r
6977     switch (LOWORD(wParam)) {\r
6978     case IDM_QuickPaste:\r
6979       {\r
6980         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6981         if (sel.cpMin == sel.cpMax) {\r
6982           MessageBeep(MB_ICONEXCLAMATION);\r
6983           return 0;\r
6984         }\r
6985         SendMessage(hwnd, WM_COPY, 0, 0);\r
6986         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6987         SendMessage(hInput, WM_PASTE, 0, 0);\r
6988         SetFocus(hInput);\r
6989         return 0;\r
6990       }\r
6991     case IDM_Cut:\r
6992       SendMessage(hwnd, WM_CUT, 0, 0);\r
6993       return 0;\r
6994     case IDM_Paste:\r
6995       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6996       return 0;\r
6997     case IDM_Copy:\r
6998       SendMessage(hwnd, WM_COPY, 0, 0);\r
6999       return 0;\r
7000     default:\r
7001       {\r
7002         int i = LOWORD(wParam) - IDM_CommandX;\r
7003         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7004             icsTextMenuEntry[i].command != NULL) {\r
7005           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7006                    icsTextMenuEntry[i].getname,\r
7007                    icsTextMenuEntry[i].immediate);\r
7008           return 0;\r
7009         }\r
7010       }\r
7011       break;\r
7012     }\r
7013     break;\r
7014   }\r
7015   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7016 }\r
7017 \r
7018 WNDPROC consoleInputWindowProc;\r
7019 \r
7020 LRESULT CALLBACK\r
7021 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7022 {\r
7023   char buf[MSG_SIZ];\r
7024   char *p;\r
7025   static BOOL sendNextChar = FALSE;\r
7026   static BOOL quoteNextChar = FALSE;\r
7027   InputSource *is = consoleInputSource;\r
7028   CHARFORMAT cf;\r
7029   CHARRANGE sel;\r
7030 \r
7031   switch (message) {\r
7032   case WM_CHAR:\r
7033     if (!appData.localLineEditing || sendNextChar) {\r
7034       is->buf[0] = (CHAR) wParam;\r
7035       is->count = 1;\r
7036       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7037       sendNextChar = FALSE;\r
7038       return 0;\r
7039     }\r
7040     if (quoteNextChar) {\r
7041       buf[0] = (char) wParam;\r
7042       buf[1] = NULLCHAR;\r
7043       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7044       quoteNextChar = FALSE;\r
7045       return 0;\r
7046     }\r
7047     switch (wParam) {\r
7048     case '\r':   /* Enter key */\r
7049       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7050       if (consoleEcho) SaveInHistory(is->buf);\r
7051       is->buf[is->count++] = '\n';\r
7052       is->buf[is->count] = NULLCHAR;\r
7053       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7054       if (consoleEcho) {\r
7055         ConsoleOutput(is->buf, is->count, TRUE);\r
7056       } else if (appData.localLineEditing) {\r
7057         ConsoleOutput("\n", 1, TRUE);\r
7058       }\r
7059       /* fall thru */\r
7060     case '\033': /* Escape key */\r
7061       SetWindowText(hwnd, "");\r
7062       cf.cbSize = sizeof(CHARFORMAT);\r
7063       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7064       if (consoleEcho) {\r
7065         cf.crTextColor = textAttribs[ColorNormal].color;\r
7066       } else {\r
7067         cf.crTextColor = COLOR_ECHOOFF;\r
7068       }\r
7069       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7070       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7071       return 0;\r
7072     case '\t':   /* Tab key */\r
7073       if (GetKeyState(VK_SHIFT) < 0) {\r
7074         /* shifted */\r
7075         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7076       } else {\r
7077         /* unshifted */\r
7078         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7079         if (buttonDesc[0].hwnd) {\r
7080           SetFocus(buttonDesc[0].hwnd);\r
7081         } else {\r
7082           SetFocus(hwndMain);\r
7083         }\r
7084       }\r
7085       return 0;\r
7086     case '\023': /* Ctrl+S */\r
7087       sendNextChar = TRUE;\r
7088       return 0;\r
7089     case '\021': /* Ctrl+Q */\r
7090       quoteNextChar = TRUE;\r
7091       return 0;\r
7092     JAWS_REPLAY\r
7093     default:\r
7094       break;\r
7095     }\r
7096     break;\r
7097   case WM_KEYDOWN:\r
7098     switch (wParam) {\r
7099     case VK_UP:\r
7100       GetWindowText(hwnd, buf, MSG_SIZ);\r
7101       p = PrevInHistory(buf);\r
7102       if (p != NULL) {\r
7103         SetWindowText(hwnd, p);\r
7104         sel.cpMin = 999999;\r
7105         sel.cpMax = 999999;\r
7106         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7107         return 0;\r
7108       }\r
7109       break;\r
7110     case VK_DOWN:\r
7111       p = NextInHistory();\r
7112       if (p != NULL) {\r
7113         SetWindowText(hwnd, p);\r
7114         sel.cpMin = 999999;\r
7115         sel.cpMax = 999999;\r
7116         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7117         return 0;\r
7118       }\r
7119       break;\r
7120     case VK_HOME:\r
7121     case VK_END:\r
7122       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7123       /* fall thru */\r
7124     case VK_PRIOR:\r
7125     case VK_NEXT:\r
7126       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7127       return 0;\r
7128     }\r
7129     break;\r
7130   case WM_MBUTTONDOWN:\r
7131     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7132       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7133     break;\r
7134   case WM_RBUTTONUP:\r
7135     if (GetKeyState(VK_SHIFT) & ~1) {\r
7136       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7137         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7138     } else {\r
7139       POINT pt;\r
7140       HMENU hmenu;\r
7141       hmenu = LoadMenu(hInst, "InputMenu");\r
7142       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7143       if (sel.cpMin == sel.cpMax) {\r
7144         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7145         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7146       }\r
7147       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7148         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7149       }\r
7150       pt.x = LOWORD(lParam);\r
7151       pt.y = HIWORD(lParam);\r
7152       MenuPopup(hwnd, pt, hmenu, -1);\r
7153     }\r
7154     return 0;\r
7155   case WM_COMMAND:\r
7156     switch (LOWORD(wParam)) { \r
7157     case IDM_Undo:\r
7158       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7159       return 0;\r
7160     case IDM_SelectAll:\r
7161       sel.cpMin = 0;\r
7162       sel.cpMax = -1; /*999999?*/\r
7163       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7164       return 0;\r
7165     case IDM_Cut:\r
7166       SendMessage(hwnd, WM_CUT, 0, 0);\r
7167       return 0;\r
7168     case IDM_Paste:\r
7169       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7170       return 0;\r
7171     case IDM_Copy:\r
7172       SendMessage(hwnd, WM_COPY, 0, 0);\r
7173       return 0;\r
7174     }\r
7175     break;\r
7176   }\r
7177   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7178 }\r
7179 \r
7180 #define CO_MAX  100000\r
7181 #define CO_TRIM   1000\r
7182 \r
7183 LRESULT CALLBACK\r
7184 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7185 {\r
7186   static SnapData sd;\r
7187   HWND hText, hInput;\r
7188   RECT rect;\r
7189   static int sizeX, sizeY;\r
7190   int newSizeX, newSizeY;\r
7191   MINMAXINFO *mmi;\r
7192   WORD wMask;\r
7193 \r
7194   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7195   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7196 \r
7197   switch (message) {\r
7198   case WM_NOTIFY:\r
7199     if (((NMHDR*)lParam)->code == EN_LINK)\r
7200     {\r
7201       ENLINK *pLink = (ENLINK*)lParam;\r
7202       if (pLink->msg == WM_LBUTTONUP)\r
7203       {\r
7204         TEXTRANGE tr;\r
7205 \r
7206         tr.chrg = pLink->chrg;\r
7207         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7208         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7209         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7210         free(tr.lpstrText);\r
7211       }\r
7212     }\r
7213     break;\r
7214   case WM_INITDIALOG: /* message: initialize dialog box */\r
7215     hwndConsole = hDlg;\r
7216     SetFocus(hInput);\r
7217     consoleTextWindowProc = (WNDPROC)\r
7218       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7219     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7220     consoleInputWindowProc = (WNDPROC)\r
7221       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7222     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7223     Colorize(ColorNormal, TRUE);\r
7224     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7225     ChangedConsoleFont();\r
7226     GetClientRect(hDlg, &rect);\r
7227     sizeX = rect.right;\r
7228     sizeY = rect.bottom;\r
7229     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7230         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7231       WINDOWPLACEMENT wp;\r
7232       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7233       wp.length = sizeof(WINDOWPLACEMENT);\r
7234       wp.flags = 0;\r
7235       wp.showCmd = SW_SHOW;\r
7236       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7237       wp.rcNormalPosition.left = wpConsole.x;\r
7238       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7239       wp.rcNormalPosition.top = wpConsole.y;\r
7240       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7241       SetWindowPlacement(hDlg, &wp);\r
7242     }\r
7243 \r
7244    // [HGM] Chessknight's change 2004-07-13\r
7245    else { /* Determine Defaults */\r
7246        WINDOWPLACEMENT wp;\r
7247        wpConsole.x = wpMain.width + 1;\r
7248        wpConsole.y = wpMain.y;\r
7249        wpConsole.width = screenWidth -  wpMain.width;\r
7250        wpConsole.height = wpMain.height;\r
7251        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7252        wp.length = sizeof(WINDOWPLACEMENT);\r
7253        wp.flags = 0;\r
7254        wp.showCmd = SW_SHOW;\r
7255        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7256        wp.rcNormalPosition.left = wpConsole.x;\r
7257        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7258        wp.rcNormalPosition.top = wpConsole.y;\r
7259        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7260        SetWindowPlacement(hDlg, &wp);\r
7261     }\r
7262 \r
7263    // Allow hText to highlight URLs and send notifications on them\r
7264    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7265    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7266    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7267    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7268 \r
7269     return FALSE;\r
7270 \r
7271   case WM_SETFOCUS:\r
7272     SetFocus(hInput);\r
7273     return 0;\r
7274 \r
7275   case WM_CLOSE:\r
7276     ExitEvent(0);\r
7277     /* not reached */\r
7278     break;\r
7279 \r
7280   case WM_SIZE:\r
7281     if (IsIconic(hDlg)) break;\r
7282     newSizeX = LOWORD(lParam);\r
7283     newSizeY = HIWORD(lParam);\r
7284     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7285       RECT rectText, rectInput;\r
7286       POINT pt;\r
7287       int newTextHeight, newTextWidth;\r
7288       GetWindowRect(hText, &rectText);\r
7289       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7290       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7291       if (newTextHeight < 0) {\r
7292         newSizeY += -newTextHeight;\r
7293         newTextHeight = 0;\r
7294       }\r
7295       SetWindowPos(hText, NULL, 0, 0,\r
7296         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7297       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7298       pt.x = rectInput.left;\r
7299       pt.y = rectInput.top + newSizeY - sizeY;\r
7300       ScreenToClient(hDlg, &pt);\r
7301       SetWindowPos(hInput, NULL, \r
7302         pt.x, pt.y, /* needs client coords */   \r
7303         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7304         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7305     }\r
7306     sizeX = newSizeX;\r
7307     sizeY = newSizeY;\r
7308     break;\r
7309 \r
7310   case WM_GETMINMAXINFO:\r
7311     /* Prevent resizing window too small */\r
7312     mmi = (MINMAXINFO *) lParam;\r
7313     mmi->ptMinTrackSize.x = 100;\r
7314     mmi->ptMinTrackSize.y = 100;\r
7315     break;\r
7316 \r
7317   /* [AS] Snapping */\r
7318   case WM_ENTERSIZEMOVE:\r
7319     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7320 \r
7321   case WM_SIZING:\r
7322     return OnSizing( &sd, hDlg, wParam, lParam );\r
7323 \r
7324   case WM_MOVING:\r
7325     return OnMoving( &sd, hDlg, wParam, lParam );\r
7326 \r
7327   case WM_EXITSIZEMOVE:\r
7328         UpdateICSWidth(hText);\r
7329     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7330   }\r
7331 \r
7332   return DefWindowProc(hDlg, message, wParam, lParam);\r
7333 }\r
7334 \r
7335 \r
7336 VOID\r
7337 ConsoleCreate()\r
7338 {\r
7339   HWND hCons;\r
7340   if (hwndConsole) return;\r
7341   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7342   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7343 }\r
7344 \r
7345 \r
7346 VOID\r
7347 ConsoleOutput(char* data, int length, int forceVisible)\r
7348 {\r
7349   HWND hText;\r
7350   int trim, exlen;\r
7351   char *p, *q;\r
7352   char buf[CO_MAX+1];\r
7353   POINT pEnd;\r
7354   RECT rect;\r
7355   static int delayLF = 0;\r
7356   CHARRANGE savesel, sel;\r
7357 \r
7358   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7359   p = data;\r
7360   q = buf;\r
7361   if (delayLF) {\r
7362     *q++ = '\r';\r
7363     *q++ = '\n';\r
7364     delayLF = 0;\r
7365   }\r
7366   while (length--) {\r
7367     if (*p == '\n') {\r
7368       if (*++p) {\r
7369         *q++ = '\r';\r
7370         *q++ = '\n';\r
7371       } else {\r
7372         delayLF = 1;\r
7373       }\r
7374     } else if (*p == '\007') {\r
7375        MyPlaySound(&sounds[(int)SoundBell]);\r
7376        p++;\r
7377     } else {\r
7378       *q++ = *p++;\r
7379     }\r
7380   }\r
7381   *q = NULLCHAR;\r
7382   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7383   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7384   /* Save current selection */\r
7385   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7386   exlen = GetWindowTextLength(hText);\r
7387   /* Find out whether current end of text is visible */\r
7388   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7389   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7390   /* Trim existing text if it's too long */\r
7391   if (exlen + (q - buf) > CO_MAX) {\r
7392     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7393     sel.cpMin = 0;\r
7394     sel.cpMax = trim;\r
7395     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7396     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7397     exlen -= trim;\r
7398     savesel.cpMin -= trim;\r
7399     savesel.cpMax -= trim;\r
7400     if (exlen < 0) exlen = 0;\r
7401     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7402     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7403   }\r
7404   /* Append the new text */\r
7405   sel.cpMin = exlen;\r
7406   sel.cpMax = exlen;\r
7407   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7408   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7409   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7410   if (forceVisible || exlen == 0 ||\r
7411       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7412        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7413     /* Scroll to make new end of text visible if old end of text\r
7414        was visible or new text is an echo of user typein */\r
7415     sel.cpMin = 9999999;\r
7416     sel.cpMax = 9999999;\r
7417     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7418     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7419     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7420     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7421   }\r
7422   if (savesel.cpMax == exlen || forceVisible) {\r
7423     /* Move insert point to new end of text if it was at the old\r
7424        end of text or if the new text is an echo of user typein */\r
7425     sel.cpMin = 9999999;\r
7426     sel.cpMax = 9999999;\r
7427     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7428   } else {\r
7429     /* Restore previous selection */\r
7430     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7431   }\r
7432   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7433 }\r
7434 \r
7435 /*---------*/\r
7436 \r
7437 \r
7438 void\r
7439 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7440 {\r
7441   char buf[100];\r
7442   char *str;\r
7443   COLORREF oldFg, oldBg;\r
7444   HFONT oldFont;\r
7445   RECT rect;\r
7446 \r
7447   if(copyNumber > 1)\r
7448     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7449 \r
7450   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7451   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7452   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7453 \r
7454   rect.left = x;\r
7455   rect.right = x + squareSize;\r
7456   rect.top  = y;\r
7457   rect.bottom = y + squareSize;\r
7458   str = buf;\r
7459 \r
7460   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7461                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7462              y, ETO_CLIPPED|ETO_OPAQUE,\r
7463              &rect, str, strlen(str), NULL);\r
7464 \r
7465   (void) SetTextColor(hdc, oldFg);\r
7466   (void) SetBkColor(hdc, oldBg);\r
7467   (void) SelectObject(hdc, oldFont);\r
7468 }\r
7469 \r
7470 void\r
7471 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7472               RECT *rect, char *color, char *flagFell)\r
7473 {\r
7474   char buf[100];\r
7475   char *str;\r
7476   COLORREF oldFg, oldBg;\r
7477   HFONT oldFont;\r
7478 \r
7479   if (twoBoards && partnerUp) return;\r
7480   if (appData.clockMode) {\r
7481     if (tinyLayout)\r
7482       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7483     else\r
7484       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7485     str = buf;\r
7486   } else {\r
7487     str = color;\r
7488   }\r
7489 \r
7490   if (highlight) {\r
7491     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7492     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7493   } else {\r
7494     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7495     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7496   }\r
7497   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7498 \r
7499   JAWS_SILENCE\r
7500 \r
7501   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7502              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7503              rect, str, strlen(str), NULL);\r
7504   if(logoHeight > 0 && appData.clockMode) {\r
7505       RECT r;\r
7506       str += strlen(color)+2;\r
7507       r.top = rect->top + logoHeight/2;\r
7508       r.left = rect->left;\r
7509       r.right = rect->right;\r
7510       r.bottom = rect->bottom;\r
7511       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7512                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7513                  &r, str, strlen(str), NULL);\r
7514   }\r
7515   (void) SetTextColor(hdc, oldFg);\r
7516   (void) SetBkColor(hdc, oldBg);\r
7517   (void) SelectObject(hdc, oldFont);\r
7518 }\r
7519 \r
7520 \r
7521 int\r
7522 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7523            OVERLAPPED *ovl)\r
7524 {\r
7525   int ok, err;\r
7526 \r
7527   /* [AS]  */\r
7528   if( count <= 0 ) {\r
7529     if (appData.debugMode) {\r
7530       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7531     }\r
7532 \r
7533     return ERROR_INVALID_USER_BUFFER;\r
7534   }\r
7535 \r
7536   ResetEvent(ovl->hEvent);\r
7537   ovl->Offset = ovl->OffsetHigh = 0;\r
7538   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7539   if (ok) {\r
7540     err = NO_ERROR;\r
7541   } else {\r
7542     err = GetLastError();\r
7543     if (err == ERROR_IO_PENDING) {\r
7544       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7545       if (ok)\r
7546         err = NO_ERROR;\r
7547       else\r
7548         err = GetLastError();\r
7549     }\r
7550   }\r
7551   return err;\r
7552 }\r
7553 \r
7554 int\r
7555 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7556             OVERLAPPED *ovl)\r
7557 {\r
7558   int ok, err;\r
7559 \r
7560   ResetEvent(ovl->hEvent);\r
7561   ovl->Offset = ovl->OffsetHigh = 0;\r
7562   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7563   if (ok) {\r
7564     err = NO_ERROR;\r
7565   } else {\r
7566     err = GetLastError();\r
7567     if (err == ERROR_IO_PENDING) {\r
7568       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7569       if (ok)\r
7570         err = NO_ERROR;\r
7571       else\r
7572         err = GetLastError();\r
7573     }\r
7574   }\r
7575   return err;\r
7576 }\r
7577 \r
7578 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7579 void CheckForInputBufferFull( InputSource * is )\r
7580 {\r
7581     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7582         /* Look for end of line */\r
7583         char * p = is->buf;\r
7584         \r
7585         while( p < is->next && *p != '\n' ) {\r
7586             p++;\r
7587         }\r
7588 \r
7589         if( p >= is->next ) {\r
7590             if (appData.debugMode) {\r
7591                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7592             }\r
7593 \r
7594             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7595             is->count = (DWORD) -1;\r
7596             is->next = is->buf;\r
7597         }\r
7598     }\r
7599 }\r
7600 \r
7601 DWORD\r
7602 InputThread(LPVOID arg)\r
7603 {\r
7604   InputSource *is;\r
7605   OVERLAPPED ovl;\r
7606 \r
7607   is = (InputSource *) arg;\r
7608   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7609   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7610   while (is->hThread != NULL) {\r
7611     is->error = DoReadFile(is->hFile, is->next,\r
7612                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7613                            &is->count, &ovl);\r
7614     if (is->error == NO_ERROR) {\r
7615       is->next += is->count;\r
7616     } else {\r
7617       if (is->error == ERROR_BROKEN_PIPE) {\r
7618         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7619         is->count = 0;\r
7620       } else {\r
7621         is->count = (DWORD) -1;\r
7622         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7623         break; \r
7624       }\r
7625     }\r
7626 \r
7627     CheckForInputBufferFull( is );\r
7628 \r
7629     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7630 \r
7631     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7632 \r
7633     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7634   }\r
7635 \r
7636   CloseHandle(ovl.hEvent);\r
7637   CloseHandle(is->hFile);\r
7638 \r
7639   if (appData.debugMode) {\r
7640     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7641   }\r
7642 \r
7643   return 0;\r
7644 }\r
7645 \r
7646 \r
7647 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7648 DWORD\r
7649 NonOvlInputThread(LPVOID arg)\r
7650 {\r
7651   InputSource *is;\r
7652   char *p, *q;\r
7653   int i;\r
7654   char prev;\r
7655 \r
7656   is = (InputSource *) arg;\r
7657   while (is->hThread != NULL) {\r
7658     is->error = ReadFile(is->hFile, is->next,\r
7659                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7660                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7661     if (is->error == NO_ERROR) {\r
7662       /* Change CRLF to LF */\r
7663       if (is->next > is->buf) {\r
7664         p = is->next - 1;\r
7665         i = is->count + 1;\r
7666       } else {\r
7667         p = is->next;\r
7668         i = is->count;\r
7669       }\r
7670       q = p;\r
7671       prev = NULLCHAR;\r
7672       while (i > 0) {\r
7673         if (prev == '\r' && *p == '\n') {\r
7674           *(q-1) = '\n';\r
7675           is->count--;\r
7676         } else { \r
7677           *q++ = *p;\r
7678         }\r
7679         prev = *p++;\r
7680         i--;\r
7681       }\r
7682       *q = NULLCHAR;\r
7683       is->next = q;\r
7684     } else {\r
7685       if (is->error == ERROR_BROKEN_PIPE) {\r
7686         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7687         is->count = 0; \r
7688       } else {\r
7689         is->count = (DWORD) -1;\r
7690       }\r
7691     }\r
7692 \r
7693     CheckForInputBufferFull( is );\r
7694 \r
7695     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7696 \r
7697     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7698 \r
7699     if (is->count < 0) break;  /* Quit on error */\r
7700   }\r
7701   CloseHandle(is->hFile);\r
7702   return 0;\r
7703 }\r
7704 \r
7705 DWORD\r
7706 SocketInputThread(LPVOID arg)\r
7707 {\r
7708   InputSource *is;\r
7709 \r
7710   is = (InputSource *) arg;\r
7711   while (is->hThread != NULL) {\r
7712     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7713     if ((int)is->count == SOCKET_ERROR) {\r
7714       is->count = (DWORD) -1;\r
7715       is->error = WSAGetLastError();\r
7716     } else {\r
7717       is->error = NO_ERROR;\r
7718       is->next += is->count;\r
7719       if (is->count == 0 && is->second == is) {\r
7720         /* End of file on stderr; quit with no message */\r
7721         break;\r
7722       }\r
7723     }\r
7724     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7725 \r
7726     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7727 \r
7728     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7729   }\r
7730   return 0;\r
7731 }\r
7732 \r
7733 VOID\r
7734 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7735 {\r
7736   InputSource *is;\r
7737 \r
7738   is = (InputSource *) lParam;\r
7739   if (is->lineByLine) {\r
7740     /* Feed in lines one by one */\r
7741     char *p = is->buf;\r
7742     char *q = p;\r
7743     while (q < is->next) {\r
7744       if (*q++ == '\n') {\r
7745         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7746         p = q;\r
7747       }\r
7748     }\r
7749     \r
7750     /* Move any partial line to the start of the buffer */\r
7751     q = is->buf;\r
7752     while (p < is->next) {\r
7753       *q++ = *p++;\r
7754     }\r
7755     is->next = q;\r
7756 \r
7757     if (is->error != NO_ERROR || is->count == 0) {\r
7758       /* Notify backend of the error.  Note: If there was a partial\r
7759          line at the end, it is not flushed through. */\r
7760       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7761     }\r
7762   } else {\r
7763     /* Feed in the whole chunk of input at once */\r
7764     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7765     is->next = is->buf;\r
7766   }\r
7767 }\r
7768 \r
7769 /*---------------------------------------------------------------------------*\\r
7770  *\r
7771  *  Menu enables. Used when setting various modes.\r
7772  *\r
7773 \*---------------------------------------------------------------------------*/\r
7774 \r
7775 typedef struct {\r
7776   int item;\r
7777   int flags;\r
7778 } Enables;\r
7779 \r
7780 VOID\r
7781 GreyRevert(Boolean grey)\r
7782 { // [HGM] vari: for retracting variations in local mode\r
7783   HMENU hmenu = GetMenu(hwndMain);\r
7784   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7785   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7786 }\r
7787 \r
7788 VOID\r
7789 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7790 {\r
7791   while (enab->item > 0) {\r
7792     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7793     enab++;\r
7794   }\r
7795 }\r
7796 \r
7797 Enables gnuEnables[] = {\r
7798   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7811 \r
7812   // Needed to switch from ncp to GNU mode on Engine Load\r
7813   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7814   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7817   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7818   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7819   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7820   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7822   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7823   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7824   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7827   { -1, -1 }\r
7828 };\r
7829 \r
7830 Enables icsEnables[] = {\r
7831   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7839   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7851   { -1, -1 }\r
7852 };\r
7853 \r
7854 #if ZIPPY\r
7855 Enables zippyEnables[] = {\r
7856   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7860   { -1, -1 }\r
7861 };\r
7862 #endif\r
7863 \r
7864 Enables ncpEnables[] = {\r
7865   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7874   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7875   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7887   { -1, -1 }\r
7888 };\r
7889 \r
7890 Enables trainingOnEnables[] = {\r
7891   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7900   { -1, -1 }\r
7901 };\r
7902 \r
7903 Enables trainingOffEnables[] = {\r
7904   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7913   { -1, -1 }\r
7914 };\r
7915 \r
7916 /* These modify either ncpEnables or gnuEnables */\r
7917 Enables cmailEnables[] = {\r
7918   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7920   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7921   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7925   { -1, -1 }\r
7926 };\r
7927 \r
7928 Enables machineThinkingEnables[] = {\r
7929   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7942 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7945   { -1, -1 }\r
7946 };\r
7947 \r
7948 Enables userThinkingEnables[] = {\r
7949   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7960   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7961   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7962 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7963   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7964   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7965   { -1, -1 }\r
7966 };\r
7967 \r
7968 /*---------------------------------------------------------------------------*\\r
7969  *\r
7970  *  Front-end interface functions exported by XBoard.\r
7971  *  Functions appear in same order as prototypes in frontend.h.\r
7972  * \r
7973 \*---------------------------------------------------------------------------*/\r
7974 VOID\r
7975 CheckMark(UINT item, int state)\r
7976 {\r
7977     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7978 }\r
7979 \r
7980 VOID\r
7981 ModeHighlight()\r
7982 {\r
7983   static UINT prevChecked = 0;\r
7984   static int prevPausing = 0;\r
7985   UINT nowChecked;\r
7986 \r
7987   if (pausing != prevPausing) {\r
7988     prevPausing = pausing;\r
7989     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7990                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7991     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7992   }\r
7993 \r
7994   switch (gameMode) {\r
7995   case BeginningOfGame:\r
7996     if (appData.icsActive)\r
7997       nowChecked = IDM_IcsClient;\r
7998     else if (appData.noChessProgram)\r
7999       nowChecked = IDM_EditGame;\r
8000     else\r
8001       nowChecked = IDM_MachineBlack;\r
8002     break;\r
8003   case MachinePlaysBlack:\r
8004     nowChecked = IDM_MachineBlack;\r
8005     break;\r
8006   case MachinePlaysWhite:\r
8007     nowChecked = IDM_MachineWhite;\r
8008     break;\r
8009   case TwoMachinesPlay:\r
8010     nowChecked = IDM_TwoMachines;\r
8011     break;\r
8012   case AnalyzeMode:\r
8013     nowChecked = IDM_AnalysisMode;\r
8014     break;\r
8015   case AnalyzeFile:\r
8016     nowChecked = IDM_AnalyzeFile;\r
8017     break;\r
8018   case EditGame:\r
8019     nowChecked = IDM_EditGame;\r
8020     break;\r
8021   case PlayFromGameFile:\r
8022     nowChecked = IDM_LoadGame;\r
8023     break;\r
8024   case EditPosition:\r
8025     nowChecked = IDM_EditPosition;\r
8026     break;\r
8027   case Training:\r
8028     nowChecked = IDM_Training;\r
8029     break;\r
8030   case IcsPlayingWhite:\r
8031   case IcsPlayingBlack:\r
8032   case IcsObserving:\r
8033   case IcsIdle:\r
8034     nowChecked = IDM_IcsClient;\r
8035     break;\r
8036   default:\r
8037   case EndOfGame:\r
8038     nowChecked = 0;\r
8039     break;\r
8040   }\r
8041   CheckMark(prevChecked, MF_UNCHECKED);\r
8042   CheckMark(nowChecked, MF_CHECKED);\r
8043   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8044 \r
8045   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8046     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8047                           MF_BYCOMMAND|MF_ENABLED);\r
8048   } else {\r
8049     (void) EnableMenuItem(GetMenu(hwndMain), \r
8050                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8051   }\r
8052 \r
8053   prevChecked = nowChecked;\r
8054 \r
8055   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8056   if (appData.icsActive) {\r
8057        if (appData.icsEngineAnalyze) {\r
8058                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8059        } else {\r
8060                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8061        }\r
8062   }\r
8063   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8064 }\r
8065 \r
8066 VOID\r
8067 SetICSMode()\r
8068 {\r
8069   HMENU hmenu = GetMenu(hwndMain);\r
8070   SetMenuEnables(hmenu, icsEnables);\r
8071   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8072     MF_BYCOMMAND|MF_ENABLED);\r
8073 #if ZIPPY\r
8074   if (appData.zippyPlay) {\r
8075     SetMenuEnables(hmenu, zippyEnables);\r
8076     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8077          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8078           MF_BYCOMMAND|MF_ENABLED);\r
8079   }\r
8080 #endif\r
8081 }\r
8082 \r
8083 VOID\r
8084 SetGNUMode()\r
8085 {\r
8086   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8087 }\r
8088 \r
8089 VOID\r
8090 SetNCPMode()\r
8091 {\r
8092   HMENU hmenu = GetMenu(hwndMain);\r
8093   SetMenuEnables(hmenu, ncpEnables);\r
8094     DrawMenuBar(hwndMain);\r
8095 }\r
8096 \r
8097 VOID\r
8098 SetCmailMode()\r
8099 {\r
8100   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8101 }\r
8102 \r
8103 VOID \r
8104 SetTrainingModeOn()\r
8105 {\r
8106   int i;\r
8107   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8108   for (i = 0; i < N_BUTTONS; i++) {\r
8109     if (buttonDesc[i].hwnd != NULL)\r
8110       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8111   }\r
8112   CommentPopDown();\r
8113 }\r
8114 \r
8115 VOID SetTrainingModeOff()\r
8116 {\r
8117   int i;\r
8118   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8119   for (i = 0; i < N_BUTTONS; i++) {\r
8120     if (buttonDesc[i].hwnd != NULL)\r
8121       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8122   }\r
8123 }\r
8124 \r
8125 \r
8126 VOID\r
8127 SetUserThinkingEnables()\r
8128 {\r
8129   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8130 }\r
8131 \r
8132 VOID\r
8133 SetMachineThinkingEnables()\r
8134 {\r
8135   HMENU hMenu = GetMenu(hwndMain);\r
8136   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8137 \r
8138   SetMenuEnables(hMenu, machineThinkingEnables);\r
8139 \r
8140   if (gameMode == MachinePlaysBlack) {\r
8141     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8142   } else if (gameMode == MachinePlaysWhite) {\r
8143     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8144   } else if (gameMode == TwoMachinesPlay) {\r
8145     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8146   }\r
8147 }\r
8148 \r
8149 \r
8150 VOID\r
8151 DisplayTitle(char *str)\r
8152 {\r
8153   char title[MSG_SIZ], *host;\r
8154   if (str[0] != NULLCHAR) {\r
8155     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8156   } else if (appData.icsActive) {\r
8157     if (appData.icsCommPort[0] != NULLCHAR)\r
8158       host = "ICS";\r
8159     else \r
8160       host = appData.icsHost;\r
8161       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8162   } else if (appData.noChessProgram) {\r
8163     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8164   } else {\r
8165     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8166     strcat(title, ": ");\r
8167     strcat(title, first.tidy);\r
8168   }\r
8169   SetWindowText(hwndMain, title);\r
8170 }\r
8171 \r
8172 \r
8173 VOID\r
8174 DisplayMessage(char *str1, char *str2)\r
8175 {\r
8176   HDC hdc;\r
8177   HFONT oldFont;\r
8178   int remain = MESSAGE_TEXT_MAX - 1;\r
8179   int len;\r
8180 \r
8181   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8182   messageText[0] = NULLCHAR;\r
8183   if (*str1) {\r
8184     len = strlen(str1);\r
8185     if (len > remain) len = remain;\r
8186     strncpy(messageText, str1, len);\r
8187     messageText[len] = NULLCHAR;\r
8188     remain -= len;\r
8189   }\r
8190   if (*str2 && remain >= 2) {\r
8191     if (*str1) {\r
8192       strcat(messageText, "  ");\r
8193       remain -= 2;\r
8194     }\r
8195     len = strlen(str2);\r
8196     if (len > remain) len = remain;\r
8197     strncat(messageText, str2, len);\r
8198   }\r
8199   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8200   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8201 \r
8202   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8203 \r
8204   SAYMACHINEMOVE();\r
8205 \r
8206   hdc = GetDC(hwndMain);\r
8207   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8208   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8209              &messageRect, messageText, strlen(messageText), NULL);\r
8210   (void) SelectObject(hdc, oldFont);\r
8211   (void) ReleaseDC(hwndMain, hdc);\r
8212 }\r
8213 \r
8214 VOID\r
8215 DisplayError(char *str, int error)\r
8216 {\r
8217   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8218   int len;\r
8219 \r
8220   if (error == 0) {\r
8221     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8222   } else {\r
8223     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8224                         NULL, error, LANG_NEUTRAL,\r
8225                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8226     if (len > 0) {\r
8227       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8228     } else {\r
8229       ErrorMap *em = errmap;\r
8230       while (em->err != 0 && em->err != error) em++;\r
8231       if (em->err != 0) {\r
8232         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8233       } else {\r
8234         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8235       }\r
8236     }\r
8237   }\r
8238   \r
8239   ErrorPopUp(_("Error"), buf);\r
8240 }\r
8241 \r
8242 \r
8243 VOID\r
8244 DisplayMoveError(char *str)\r
8245 {\r
8246   fromX = fromY = -1;\r
8247   ClearHighlights();\r
8248   DrawPosition(FALSE, NULL);\r
8249   if (appData.popupMoveErrors) {\r
8250     ErrorPopUp(_("Error"), str);\r
8251   } else {\r
8252     DisplayMessage(str, "");\r
8253     moveErrorMessageUp = TRUE;\r
8254   }\r
8255 }\r
8256 \r
8257 VOID\r
8258 DisplayFatalError(char *str, int error, int exitStatus)\r
8259 {\r
8260   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8261   int len;\r
8262   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8263 \r
8264   if (error != 0) {\r
8265     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8266                         NULL, error, LANG_NEUTRAL,\r
8267                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8268     if (len > 0) {\r
8269       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8270     } else {\r
8271       ErrorMap *em = errmap;\r
8272       while (em->err != 0 && em->err != error) em++;\r
8273       if (em->err != 0) {\r
8274         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8275       } else {\r
8276         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8277       }\r
8278     }\r
8279     str = buf;\r
8280   }\r
8281   if (appData.debugMode) {\r
8282     fprintf(debugFP, "%s: %s\n", label, str);\r
8283   }\r
8284   if (appData.popupExitMessage) {\r
8285     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8286                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8287   }\r
8288   ExitEvent(exitStatus);\r
8289 }\r
8290 \r
8291 \r
8292 VOID\r
8293 DisplayInformation(char *str)\r
8294 {\r
8295   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8296 }\r
8297 \r
8298 \r
8299 VOID\r
8300 DisplayNote(char *str)\r
8301 {\r
8302   ErrorPopUp(_("Note"), str);\r
8303 }\r
8304 \r
8305 \r
8306 typedef struct {\r
8307   char *title, *question, *replyPrefix;\r
8308   ProcRef pr;\r
8309 } QuestionParams;\r
8310 \r
8311 LRESULT CALLBACK\r
8312 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8313 {\r
8314   static QuestionParams *qp;\r
8315   char reply[MSG_SIZ];\r
8316   int len, err;\r
8317 \r
8318   switch (message) {\r
8319   case WM_INITDIALOG:\r
8320     qp = (QuestionParams *) lParam;\r
8321     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8322     Translate(hDlg, DLG_Question);\r
8323     SetWindowText(hDlg, qp->title);\r
8324     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8325     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8326     return FALSE;\r
8327 \r
8328   case WM_COMMAND:\r
8329     switch (LOWORD(wParam)) {\r
8330     case IDOK:\r
8331       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8332       if (*reply) strcat(reply, " ");\r
8333       len = strlen(reply);\r
8334       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8335       strcat(reply, "\n");\r
8336       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8337       EndDialog(hDlg, TRUE);\r
8338       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8339       return TRUE;\r
8340     case IDCANCEL:\r
8341       EndDialog(hDlg, FALSE);\r
8342       return TRUE;\r
8343     default:\r
8344       break;\r
8345     }\r
8346     break;\r
8347   }\r
8348   return FALSE;\r
8349 }\r
8350 \r
8351 VOID\r
8352 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8353 {\r
8354     QuestionParams qp;\r
8355     FARPROC lpProc;\r
8356     \r
8357     qp.title = title;\r
8358     qp.question = question;\r
8359     qp.replyPrefix = replyPrefix;\r
8360     qp.pr = pr;\r
8361     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8362     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8363       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8364     FreeProcInstance(lpProc);\r
8365 }\r
8366 \r
8367 /* [AS] Pick FRC position */\r
8368 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8369 {\r
8370     static int * lpIndexFRC;\r
8371     BOOL index_is_ok;\r
8372     char buf[16];\r
8373 \r
8374     switch( message )\r
8375     {\r
8376     case WM_INITDIALOG:\r
8377         lpIndexFRC = (int *) lParam;\r
8378 \r
8379         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8380         Translate(hDlg, DLG_NewGameFRC);\r
8381 \r
8382         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8383         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8384         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8385         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8386 \r
8387         break;\r
8388 \r
8389     case WM_COMMAND:\r
8390         switch( LOWORD(wParam) ) {\r
8391         case IDOK:\r
8392             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8393             EndDialog( hDlg, 0 );\r
8394             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8395             return TRUE;\r
8396         case IDCANCEL:\r
8397             EndDialog( hDlg, 1 );   \r
8398             return TRUE;\r
8399         case IDC_NFG_Edit:\r
8400             if( HIWORD(wParam) == EN_CHANGE ) {\r
8401                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8402 \r
8403                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8404             }\r
8405             return TRUE;\r
8406         case IDC_NFG_Random:\r
8407           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8408             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8409             return TRUE;\r
8410         }\r
8411 \r
8412         break;\r
8413     }\r
8414 \r
8415     return FALSE;\r
8416 }\r
8417 \r
8418 int NewGameFRC()\r
8419 {\r
8420     int result;\r
8421     int index = appData.defaultFrcPosition;\r
8422     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8423 \r
8424     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8425 \r
8426     if( result == 0 ) {\r
8427         appData.defaultFrcPosition = index;\r
8428     }\r
8429 \r
8430     return result;\r
8431 }\r
8432 \r
8433 /* [AS] Game list options. Refactored by HGM */\r
8434 \r
8435 HWND gameListOptionsDialog;\r
8436 \r
8437 // low-level front-end: clear text edit / list widget\r
8438 void\r
8439 GLT_ClearList()\r
8440 {\r
8441     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8442 }\r
8443 \r
8444 // low-level front-end: clear text edit / list widget\r
8445 void\r
8446 GLT_DeSelectList()\r
8447 {\r
8448     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8449 }\r
8450 \r
8451 // low-level front-end: append line to text edit / list widget\r
8452 void\r
8453 GLT_AddToList( char *name )\r
8454 {\r
8455     if( name != 0 ) {\r
8456             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8457     }\r
8458 }\r
8459 \r
8460 // low-level front-end: get line from text edit / list widget\r
8461 Boolean\r
8462 GLT_GetFromList( int index, char *name )\r
8463 {\r
8464     if( name != 0 ) {\r
8465             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8466                 return TRUE;\r
8467     }\r
8468     return FALSE;\r
8469 }\r
8470 \r
8471 void GLT_MoveSelection( HWND hDlg, int delta )\r
8472 {\r
8473     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8474     int idx2 = idx1 + delta;\r
8475     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8476 \r
8477     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8478         char buf[128];\r
8479 \r
8480         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8481         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8482         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8483         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8484     }\r
8485 }\r
8486 \r
8487 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8488 {\r
8489     switch( message )\r
8490     {\r
8491     case WM_INITDIALOG:\r
8492         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8493         \r
8494         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8495         Translate(hDlg, DLG_GameListOptions);\r
8496 \r
8497         /* Initialize list */\r
8498         GLT_TagsToList( lpUserGLT );\r
8499 \r
8500         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8501 \r
8502         break;\r
8503 \r
8504     case WM_COMMAND:\r
8505         switch( LOWORD(wParam) ) {\r
8506         case IDOK:\r
8507             GLT_ParseList();\r
8508             EndDialog( hDlg, 0 );\r
8509             return TRUE;\r
8510         case IDCANCEL:\r
8511             EndDialog( hDlg, 1 );\r
8512             return TRUE;\r
8513 \r
8514         case IDC_GLT_Default:\r
8515             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8516             return TRUE;\r
8517 \r
8518         case IDC_GLT_Restore:\r
8519             GLT_TagsToList( appData.gameListTags );\r
8520             return TRUE;\r
8521 \r
8522         case IDC_GLT_Up:\r
8523             GLT_MoveSelection( hDlg, -1 );\r
8524             return TRUE;\r
8525 \r
8526         case IDC_GLT_Down:\r
8527             GLT_MoveSelection( hDlg, +1 );\r
8528             return TRUE;\r
8529         }\r
8530 \r
8531         break;\r
8532     }\r
8533 \r
8534     return FALSE;\r
8535 }\r
8536 \r
8537 int GameListOptions()\r
8538 {\r
8539     int result;\r
8540     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8541 \r
8542       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8543 \r
8544     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8545 \r
8546     if( result == 0 ) {\r
8547         /* [AS] Memory leak here! */\r
8548         appData.gameListTags = strdup( lpUserGLT ); \r
8549     }\r
8550 \r
8551     return result;\r
8552 }\r
8553 \r
8554 VOID\r
8555 DisplayIcsInteractionTitle(char *str)\r
8556 {\r
8557   char consoleTitle[MSG_SIZ];\r
8558 \r
8559     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8560     SetWindowText(hwndConsole, consoleTitle);\r
8561 \r
8562     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8563       char buf[MSG_SIZ], *p = buf, *q;\r
8564         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8565       do {\r
8566         q = strchr(p, ';');\r
8567         if(q) *q++ = 0;\r
8568         if(*p) ChatPopUp(p);\r
8569       } while(p=q);\r
8570     }\r
8571 \r
8572     SetActiveWindow(hwndMain);\r
8573 }\r
8574 \r
8575 void\r
8576 DrawPosition(int fullRedraw, Board board)\r
8577 {\r
8578   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8579 }\r
8580 \r
8581 void NotifyFrontendLogin()\r
8582 {\r
8583         if (hwndConsole)\r
8584                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8585 }\r
8586 \r
8587 VOID\r
8588 ResetFrontEnd()\r
8589 {\r
8590   fromX = fromY = -1;\r
8591   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8592     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8593     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8594     dragInfo.lastpos = dragInfo.pos;\r
8595     dragInfo.start.x = dragInfo.start.y = -1;\r
8596     dragInfo.from = dragInfo.start;\r
8597     ReleaseCapture();\r
8598     DrawPosition(TRUE, NULL);\r
8599   }\r
8600   TagsPopDown();\r
8601 }\r
8602 \r
8603 \r
8604 VOID\r
8605 CommentPopUp(char *title, char *str)\r
8606 {\r
8607   HWND hwnd = GetActiveWindow();\r
8608   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8609   SAY(str);\r
8610   SetActiveWindow(hwnd);\r
8611 }\r
8612 \r
8613 VOID\r
8614 CommentPopDown(void)\r
8615 {\r
8616   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8617   if (commentDialog) {\r
8618     ShowWindow(commentDialog, SW_HIDE);\r
8619   }\r
8620   commentUp = FALSE;\r
8621 }\r
8622 \r
8623 VOID\r
8624 EditCommentPopUp(int index, char *title, char *str)\r
8625 {\r
8626   EitherCommentPopUp(index, title, str, TRUE);\r
8627 }\r
8628 \r
8629 \r
8630 VOID\r
8631 RingBell()\r
8632 {\r
8633   MyPlaySound(&sounds[(int)SoundMove]);\r
8634 }\r
8635 \r
8636 VOID PlayIcsWinSound()\r
8637 {\r
8638   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8639 }\r
8640 \r
8641 VOID PlayIcsLossSound()\r
8642 {\r
8643   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8644 }\r
8645 \r
8646 VOID PlayIcsDrawSound()\r
8647 {\r
8648   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8649 }\r
8650 \r
8651 VOID PlayIcsUnfinishedSound()\r
8652 {\r
8653   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8654 }\r
8655 \r
8656 VOID\r
8657 PlayAlarmSound()\r
8658 {\r
8659   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8660 }\r
8661 \r
8662 VOID\r
8663 PlayTellSound()\r
8664 {\r
8665   MyPlaySound(&textAttribs[ColorTell].sound);\r
8666 }\r
8667 \r
8668 \r
8669 VOID\r
8670 EchoOn()\r
8671 {\r
8672   HWND hInput;\r
8673   consoleEcho = TRUE;\r
8674   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8675   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8676   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8677 }\r
8678 \r
8679 \r
8680 VOID\r
8681 EchoOff()\r
8682 {\r
8683   CHARFORMAT cf;\r
8684   HWND hInput;\r
8685   consoleEcho = FALSE;\r
8686   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8687   /* This works OK: set text and background both to the same color */\r
8688   cf = consoleCF;\r
8689   cf.crTextColor = COLOR_ECHOOFF;\r
8690   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8691   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8692 }\r
8693 \r
8694 /* No Raw()...? */\r
8695 \r
8696 void Colorize(ColorClass cc, int continuation)\r
8697 {\r
8698   currentColorClass = cc;\r
8699   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8700   consoleCF.crTextColor = textAttribs[cc].color;\r
8701   consoleCF.dwEffects = textAttribs[cc].effects;\r
8702   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8703 }\r
8704 \r
8705 char *\r
8706 UserName()\r
8707 {\r
8708   static char buf[MSG_SIZ];\r
8709   DWORD bufsiz = MSG_SIZ;\r
8710 \r
8711   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8712         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8713   }\r
8714   if (!GetUserName(buf, &bufsiz)) {\r
8715     /*DisplayError("Error getting user name", GetLastError());*/\r
8716     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8717   }\r
8718   return buf;\r
8719 }\r
8720 \r
8721 char *\r
8722 HostName()\r
8723 {\r
8724   static char buf[MSG_SIZ];\r
8725   DWORD bufsiz = MSG_SIZ;\r
8726 \r
8727   if (!GetComputerName(buf, &bufsiz)) {\r
8728     /*DisplayError("Error getting host name", GetLastError());*/\r
8729     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8730   }\r
8731   return buf;\r
8732 }\r
8733 \r
8734 \r
8735 int\r
8736 ClockTimerRunning()\r
8737 {\r
8738   return clockTimerEvent != 0;\r
8739 }\r
8740 \r
8741 int\r
8742 StopClockTimer()\r
8743 {\r
8744   if (clockTimerEvent == 0) return FALSE;\r
8745   KillTimer(hwndMain, clockTimerEvent);\r
8746   clockTimerEvent = 0;\r
8747   return TRUE;\r
8748 }\r
8749 \r
8750 void\r
8751 StartClockTimer(long millisec)\r
8752 {\r
8753   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8754                              (UINT) millisec, NULL);\r
8755 }\r
8756 \r
8757 void\r
8758 DisplayWhiteClock(long timeRemaining, int highlight)\r
8759 {\r
8760   HDC hdc;\r
8761   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8762 \r
8763   if(appData.noGUI) return;\r
8764   hdc = GetDC(hwndMain);\r
8765   if (!IsIconic(hwndMain)) {\r
8766     DisplayAClock(hdc, timeRemaining, highlight, \r
8767                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8768   }\r
8769   if (highlight && iconCurrent == iconBlack) {\r
8770     iconCurrent = iconWhite;\r
8771     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8772     if (IsIconic(hwndMain)) {\r
8773       DrawIcon(hdc, 2, 2, iconCurrent);\r
8774     }\r
8775   }\r
8776   (void) ReleaseDC(hwndMain, hdc);\r
8777   if (hwndConsole)\r
8778     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8779 }\r
8780 \r
8781 void\r
8782 DisplayBlackClock(long timeRemaining, int highlight)\r
8783 {\r
8784   HDC hdc;\r
8785   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8786 \r
8787   if(appData.noGUI) return;\r
8788   hdc = GetDC(hwndMain);\r
8789   if (!IsIconic(hwndMain)) {\r
8790     DisplayAClock(hdc, timeRemaining, highlight, \r
8791                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8792   }\r
8793   if (highlight && iconCurrent == iconWhite) {\r
8794     iconCurrent = iconBlack;\r
8795     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8796     if (IsIconic(hwndMain)) {\r
8797       DrawIcon(hdc, 2, 2, iconCurrent);\r
8798     }\r
8799   }\r
8800   (void) ReleaseDC(hwndMain, hdc);\r
8801   if (hwndConsole)\r
8802     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8803 }\r
8804 \r
8805 \r
8806 int\r
8807 LoadGameTimerRunning()\r
8808 {\r
8809   return loadGameTimerEvent != 0;\r
8810 }\r
8811 \r
8812 int\r
8813 StopLoadGameTimer()\r
8814 {\r
8815   if (loadGameTimerEvent == 0) return FALSE;\r
8816   KillTimer(hwndMain, loadGameTimerEvent);\r
8817   loadGameTimerEvent = 0;\r
8818   return TRUE;\r
8819 }\r
8820 \r
8821 void\r
8822 StartLoadGameTimer(long millisec)\r
8823 {\r
8824   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8825                                 (UINT) millisec, NULL);\r
8826 }\r
8827 \r
8828 void\r
8829 AutoSaveGame()\r
8830 {\r
8831   char *defName;\r
8832   FILE *f;\r
8833   char fileTitle[MSG_SIZ];\r
8834 \r
8835   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8836   f = OpenFileDialog(hwndMain, "a", defName,\r
8837                      appData.oldSaveStyle ? "gam" : "pgn",\r
8838                      GAME_FILT, \r
8839                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8840   if (f != NULL) {\r
8841     SaveGame(f, 0, "");\r
8842     fclose(f);\r
8843   }\r
8844 }\r
8845 \r
8846 \r
8847 void\r
8848 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8849 {\r
8850   if (delayedTimerEvent != 0) {\r
8851     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8852       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8853     }\r
8854     KillTimer(hwndMain, delayedTimerEvent);\r
8855     delayedTimerEvent = 0;\r
8856     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8857     delayedTimerCallback();\r
8858   }\r
8859   delayedTimerCallback = cb;\r
8860   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8861                                 (UINT) millisec, NULL);\r
8862 }\r
8863 \r
8864 DelayedEventCallback\r
8865 GetDelayedEvent()\r
8866 {\r
8867   if (delayedTimerEvent) {\r
8868     return delayedTimerCallback;\r
8869   } else {\r
8870     return NULL;\r
8871   }\r
8872 }\r
8873 \r
8874 void\r
8875 CancelDelayedEvent()\r
8876 {\r
8877   if (delayedTimerEvent) {\r
8878     KillTimer(hwndMain, delayedTimerEvent);\r
8879     delayedTimerEvent = 0;\r
8880   }\r
8881 }\r
8882 \r
8883 DWORD GetWin32Priority(int nice)\r
8884 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8885 /*\r
8886 REALTIME_PRIORITY_CLASS     0x00000100\r
8887 HIGH_PRIORITY_CLASS         0x00000080\r
8888 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8889 NORMAL_PRIORITY_CLASS       0x00000020\r
8890 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8891 IDLE_PRIORITY_CLASS         0x00000040\r
8892 */\r
8893         if (nice < -15) return 0x00000080;\r
8894         if (nice < 0)   return 0x00008000;\r
8895         if (nice == 0)  return 0x00000020;\r
8896         if (nice < 15)  return 0x00004000;\r
8897         return 0x00000040;\r
8898 }\r
8899 \r
8900 void RunCommand(char *cmdLine)\r
8901 {\r
8902   /* Now create the child process. */\r
8903   STARTUPINFO siStartInfo;\r
8904   PROCESS_INFORMATION piProcInfo;\r
8905 \r
8906   siStartInfo.cb = sizeof(STARTUPINFO);\r
8907   siStartInfo.lpReserved = NULL;\r
8908   siStartInfo.lpDesktop = NULL;\r
8909   siStartInfo.lpTitle = NULL;\r
8910   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8911   siStartInfo.cbReserved2 = 0;\r
8912   siStartInfo.lpReserved2 = NULL;\r
8913   siStartInfo.hStdInput = NULL;\r
8914   siStartInfo.hStdOutput = NULL;\r
8915   siStartInfo.hStdError = NULL;\r
8916 \r
8917   CreateProcess(NULL,\r
8918                 cmdLine,           /* command line */\r
8919                 NULL,      /* process security attributes */\r
8920                 NULL,      /* primary thread security attrs */\r
8921                 TRUE,      /* handles are inherited */\r
8922                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8923                 NULL,      /* use parent's environment */\r
8924                 NULL,\r
8925                 &siStartInfo, /* STARTUPINFO pointer */\r
8926                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8927 \r
8928   CloseHandle(piProcInfo.hThread);\r
8929 }\r
8930 \r
8931 /* Start a child process running the given program.\r
8932    The process's standard output can be read from "from", and its\r
8933    standard input can be written to "to".\r
8934    Exit with fatal error if anything goes wrong.\r
8935    Returns an opaque pointer that can be used to destroy the process\r
8936    later.\r
8937 */\r
8938 int\r
8939 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8940 {\r
8941 #define BUFSIZE 4096\r
8942 \r
8943   HANDLE hChildStdinRd, hChildStdinWr,\r
8944     hChildStdoutRd, hChildStdoutWr;\r
8945   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8946   SECURITY_ATTRIBUTES saAttr;\r
8947   BOOL fSuccess;\r
8948   PROCESS_INFORMATION piProcInfo;\r
8949   STARTUPINFO siStartInfo;\r
8950   ChildProc *cp;\r
8951   char buf[MSG_SIZ];\r
8952   DWORD err;\r
8953 \r
8954   if (appData.debugMode) {\r
8955     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8956   }\r
8957 \r
8958   *pr = NoProc;\r
8959 \r
8960   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8961   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8962   saAttr.bInheritHandle = TRUE;\r
8963   saAttr.lpSecurityDescriptor = NULL;\r
8964 \r
8965   /*\r
8966    * The steps for redirecting child's STDOUT:\r
8967    *     1. Create anonymous pipe to be STDOUT for child.\r
8968    *     2. Create a noninheritable duplicate of read handle,\r
8969    *         and close the inheritable read handle.\r
8970    */\r
8971 \r
8972   /* Create a pipe for the child's STDOUT. */\r
8973   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8974     return GetLastError();\r
8975   }\r
8976 \r
8977   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8978   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8979                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8980                              FALSE,     /* not inherited */\r
8981                              DUPLICATE_SAME_ACCESS);\r
8982   if (! fSuccess) {\r
8983     return GetLastError();\r
8984   }\r
8985   CloseHandle(hChildStdoutRd);\r
8986 \r
8987   /*\r
8988    * The steps for redirecting child's STDIN:\r
8989    *     1. Create anonymous pipe to be STDIN for child.\r
8990    *     2. Create a noninheritable duplicate of write handle,\r
8991    *         and close the inheritable write handle.\r
8992    */\r
8993 \r
8994   /* Create a pipe for the child's STDIN. */\r
8995   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8996     return GetLastError();\r
8997   }\r
8998 \r
8999   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9000   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9001                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9002                              FALSE,     /* not inherited */\r
9003                              DUPLICATE_SAME_ACCESS);\r
9004   if (! fSuccess) {\r
9005     return GetLastError();\r
9006   }\r
9007   CloseHandle(hChildStdinWr);\r
9008 \r
9009   /* Arrange to (1) look in dir for the child .exe file, and\r
9010    * (2) have dir be the child's working directory.  Interpret\r
9011    * dir relative to the directory WinBoard loaded from. */\r
9012   GetCurrentDirectory(MSG_SIZ, buf);\r
9013   SetCurrentDirectory(installDir);\r
9014   SetCurrentDirectory(dir);\r
9015 \r
9016   /* Now create the child process. */\r
9017 \r
9018   siStartInfo.cb = sizeof(STARTUPINFO);\r
9019   siStartInfo.lpReserved = NULL;\r
9020   siStartInfo.lpDesktop = NULL;\r
9021   siStartInfo.lpTitle = NULL;\r
9022   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9023   siStartInfo.cbReserved2 = 0;\r
9024   siStartInfo.lpReserved2 = NULL;\r
9025   siStartInfo.hStdInput = hChildStdinRd;\r
9026   siStartInfo.hStdOutput = hChildStdoutWr;\r
9027   siStartInfo.hStdError = hChildStdoutWr;\r
9028 \r
9029   fSuccess = CreateProcess(NULL,\r
9030                            cmdLine,        /* command line */\r
9031                            NULL,           /* process security attributes */\r
9032                            NULL,           /* primary thread security attrs */\r
9033                            TRUE,           /* handles are inherited */\r
9034                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9035                            NULL,           /* use parent's environment */\r
9036                            NULL,\r
9037                            &siStartInfo, /* STARTUPINFO pointer */\r
9038                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9039 \r
9040   err = GetLastError();\r
9041   SetCurrentDirectory(buf); /* return to prev directory */\r
9042   if (! fSuccess) {\r
9043     return err;\r
9044   }\r
9045 \r
9046   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9047     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9048     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9049   }\r
9050 \r
9051   /* Close the handles we don't need in the parent */\r
9052   CloseHandle(piProcInfo.hThread);\r
9053   CloseHandle(hChildStdinRd);\r
9054   CloseHandle(hChildStdoutWr);\r
9055 \r
9056   /* Prepare return value */\r
9057   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9058   cp->kind = CPReal;\r
9059   cp->hProcess = piProcInfo.hProcess;\r
9060   cp->pid = piProcInfo.dwProcessId;\r
9061   cp->hFrom = hChildStdoutRdDup;\r
9062   cp->hTo = hChildStdinWrDup;\r
9063 \r
9064   *pr = (void *) cp;\r
9065 \r
9066   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9067      2000 where engines sometimes don't see the initial command(s)\r
9068      from WinBoard and hang.  I don't understand how that can happen,\r
9069      but the Sleep is harmless, so I've put it in.  Others have also\r
9070      reported what may be the same problem, so hopefully this will fix\r
9071      it for them too.  */\r
9072   Sleep(500);\r
9073 \r
9074   return NO_ERROR;\r
9075 }\r
9076 \r
9077 \r
9078 void\r
9079 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9080 {\r
9081   ChildProc *cp; int result;\r
9082 \r
9083   cp = (ChildProc *) pr;\r
9084   if (cp == NULL) return;\r
9085 \r
9086   switch (cp->kind) {\r
9087   case CPReal:\r
9088     /* TerminateProcess is considered harmful, so... */\r
9089     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9090     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9091     /* The following doesn't work because the chess program\r
9092        doesn't "have the same console" as WinBoard.  Maybe\r
9093        we could arrange for this even though neither WinBoard\r
9094        nor the chess program uses a console for stdio? */\r
9095     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9096 \r
9097     /* [AS] Special termination modes for misbehaving programs... */\r
9098     if( signal == 9 ) { \r
9099         result = TerminateProcess( cp->hProcess, 0 );\r
9100 \r
9101         if ( appData.debugMode) {\r
9102             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9103         }\r
9104     }\r
9105     else if( signal == 10 ) {\r
9106         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9107 \r
9108         if( dw != WAIT_OBJECT_0 ) {\r
9109             result = TerminateProcess( cp->hProcess, 0 );\r
9110 \r
9111             if ( appData.debugMode) {\r
9112                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9113             }\r
9114 \r
9115         }\r
9116     }\r
9117 \r
9118     CloseHandle(cp->hProcess);\r
9119     break;\r
9120 \r
9121   case CPComm:\r
9122     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9123     break;\r
9124 \r
9125   case CPSock:\r
9126     closesocket(cp->sock);\r
9127     WSACleanup();\r
9128     break;\r
9129 \r
9130   case CPRcmd:\r
9131     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9132     closesocket(cp->sock);\r
9133     closesocket(cp->sock2);\r
9134     WSACleanup();\r
9135     break;\r
9136   }\r
9137   free(cp);\r
9138 }\r
9139 \r
9140 void\r
9141 InterruptChildProcess(ProcRef pr)\r
9142 {\r
9143   ChildProc *cp;\r
9144 \r
9145   cp = (ChildProc *) pr;\r
9146   if (cp == NULL) return;\r
9147   switch (cp->kind) {\r
9148   case CPReal:\r
9149     /* The following doesn't work because the chess program\r
9150        doesn't "have the same console" as WinBoard.  Maybe\r
9151        we could arrange for this even though neither WinBoard\r
9152        nor the chess program uses a console for stdio */\r
9153     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9154     break;\r
9155 \r
9156   case CPComm:\r
9157   case CPSock:\r
9158     /* Can't interrupt */\r
9159     break;\r
9160 \r
9161   case CPRcmd:\r
9162     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9163     break;\r
9164   }\r
9165 }\r
9166 \r
9167 \r
9168 int\r
9169 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9170 {\r
9171   char cmdLine[MSG_SIZ];\r
9172 \r
9173   if (port[0] == NULLCHAR) {\r
9174     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9175   } else {\r
9176     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9177   }\r
9178   return StartChildProcess(cmdLine, "", pr);\r
9179 }\r
9180 \r
9181 \r
9182 /* Code to open TCP sockets */\r
9183 \r
9184 int\r
9185 OpenTCP(char *host, char *port, ProcRef *pr)\r
9186 {\r
9187   ChildProc *cp;\r
9188   int err;\r
9189   SOCKET s;\r
9190 \r
9191   struct sockaddr_in sa, mysa;\r
9192   struct hostent FAR *hp;\r
9193   unsigned short uport;\r
9194   WORD wVersionRequested;\r
9195   WSADATA wsaData;\r
9196 \r
9197   /* Initialize socket DLL */\r
9198   wVersionRequested = MAKEWORD(1, 1);\r
9199   err = WSAStartup(wVersionRequested, &wsaData);\r
9200   if (err != 0) return err;\r
9201 \r
9202   /* Make socket */\r
9203   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9204     err = WSAGetLastError();\r
9205     WSACleanup();\r
9206     return err;\r
9207   }\r
9208 \r
9209   /* Bind local address using (mostly) don't-care values.\r
9210    */\r
9211   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9212   mysa.sin_family = AF_INET;\r
9213   mysa.sin_addr.s_addr = INADDR_ANY;\r
9214   uport = (unsigned short) 0;\r
9215   mysa.sin_port = htons(uport);\r
9216   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9217       == SOCKET_ERROR) {\r
9218     err = WSAGetLastError();\r
9219     WSACleanup();\r
9220     return err;\r
9221   }\r
9222 \r
9223   /* Resolve remote host name */\r
9224   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9225   if (!(hp = gethostbyname(host))) {\r
9226     unsigned int b0, b1, b2, b3;\r
9227 \r
9228     err = WSAGetLastError();\r
9229 \r
9230     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9231       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9232       hp->h_addrtype = AF_INET;\r
9233       hp->h_length = 4;\r
9234       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9235       hp->h_addr_list[0] = (char *) malloc(4);\r
9236       hp->h_addr_list[0][0] = (char) b0;\r
9237       hp->h_addr_list[0][1] = (char) b1;\r
9238       hp->h_addr_list[0][2] = (char) b2;\r
9239       hp->h_addr_list[0][3] = (char) b3;\r
9240     } else {\r
9241       WSACleanup();\r
9242       return err;\r
9243     }\r
9244   }\r
9245   sa.sin_family = hp->h_addrtype;\r
9246   uport = (unsigned short) atoi(port);\r
9247   sa.sin_port = htons(uport);\r
9248   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9249 \r
9250   /* Make connection */\r
9251   if (connect(s, (struct sockaddr *) &sa,\r
9252               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9253     err = WSAGetLastError();\r
9254     WSACleanup();\r
9255     return err;\r
9256   }\r
9257 \r
9258   /* Prepare return value */\r
9259   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9260   cp->kind = CPSock;\r
9261   cp->sock = s;\r
9262   *pr = (ProcRef *) cp;\r
9263 \r
9264   return NO_ERROR;\r
9265 }\r
9266 \r
9267 int\r
9268 OpenCommPort(char *name, ProcRef *pr)\r
9269 {\r
9270   HANDLE h;\r
9271   COMMTIMEOUTS ct;\r
9272   ChildProc *cp;\r
9273   char fullname[MSG_SIZ];\r
9274 \r
9275   if (*name != '\\')\r
9276     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9277   else\r
9278     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9279 \r
9280   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9281                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9282   if (h == (HANDLE) -1) {\r
9283     return GetLastError();\r
9284   }\r
9285   hCommPort = h;\r
9286 \r
9287   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9288 \r
9289   /* Accumulate characters until a 100ms pause, then parse */\r
9290   ct.ReadIntervalTimeout = 100;\r
9291   ct.ReadTotalTimeoutMultiplier = 0;\r
9292   ct.ReadTotalTimeoutConstant = 0;\r
9293   ct.WriteTotalTimeoutMultiplier = 0;\r
9294   ct.WriteTotalTimeoutConstant = 0;\r
9295   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9296 \r
9297   /* Prepare return value */\r
9298   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9299   cp->kind = CPComm;\r
9300   cp->hFrom = h;\r
9301   cp->hTo = h;\r
9302   *pr = (ProcRef *) cp;\r
9303 \r
9304   return NO_ERROR;\r
9305 }\r
9306 \r
9307 int\r
9308 OpenLoopback(ProcRef *pr)\r
9309 {\r
9310   DisplayFatalError(_("Not implemented"), 0, 1);\r
9311   return NO_ERROR;\r
9312 }\r
9313 \r
9314 \r
9315 int\r
9316 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9317 {\r
9318   ChildProc *cp;\r
9319   int err;\r
9320   SOCKET s, s2, s3;\r
9321   struct sockaddr_in sa, mysa;\r
9322   struct hostent FAR *hp;\r
9323   unsigned short uport;\r
9324   WORD wVersionRequested;\r
9325   WSADATA wsaData;\r
9326   int fromPort;\r
9327   char stderrPortStr[MSG_SIZ];\r
9328 \r
9329   /* Initialize socket DLL */\r
9330   wVersionRequested = MAKEWORD(1, 1);\r
9331   err = WSAStartup(wVersionRequested, &wsaData);\r
9332   if (err != 0) return err;\r
9333 \r
9334   /* Resolve remote host name */\r
9335   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9336   if (!(hp = gethostbyname(host))) {\r
9337     unsigned int b0, b1, b2, b3;\r
9338 \r
9339     err = WSAGetLastError();\r
9340 \r
9341     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9342       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9343       hp->h_addrtype = AF_INET;\r
9344       hp->h_length = 4;\r
9345       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9346       hp->h_addr_list[0] = (char *) malloc(4);\r
9347       hp->h_addr_list[0][0] = (char) b0;\r
9348       hp->h_addr_list[0][1] = (char) b1;\r
9349       hp->h_addr_list[0][2] = (char) b2;\r
9350       hp->h_addr_list[0][3] = (char) b3;\r
9351     } else {\r
9352       WSACleanup();\r
9353       return err;\r
9354     }\r
9355   }\r
9356   sa.sin_family = hp->h_addrtype;\r
9357   uport = (unsigned short) 514;\r
9358   sa.sin_port = htons(uport);\r
9359   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9360 \r
9361   /* Bind local socket to unused "privileged" port address\r
9362    */\r
9363   s = INVALID_SOCKET;\r
9364   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9365   mysa.sin_family = AF_INET;\r
9366   mysa.sin_addr.s_addr = INADDR_ANY;\r
9367   for (fromPort = 1023;; fromPort--) {\r
9368     if (fromPort < 0) {\r
9369       WSACleanup();\r
9370       return WSAEADDRINUSE;\r
9371     }\r
9372     if (s == INVALID_SOCKET) {\r
9373       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9374         err = WSAGetLastError();\r
9375         WSACleanup();\r
9376         return err;\r
9377       }\r
9378     }\r
9379     uport = (unsigned short) fromPort;\r
9380     mysa.sin_port = htons(uport);\r
9381     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9382         == SOCKET_ERROR) {\r
9383       err = WSAGetLastError();\r
9384       if (err == WSAEADDRINUSE) continue;\r
9385       WSACleanup();\r
9386       return err;\r
9387     }\r
9388     if (connect(s, (struct sockaddr *) &sa,\r
9389       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9390       err = WSAGetLastError();\r
9391       if (err == WSAEADDRINUSE) {\r
9392         closesocket(s);\r
9393         s = -1;\r
9394         continue;\r
9395       }\r
9396       WSACleanup();\r
9397       return err;\r
9398     }\r
9399     break;\r
9400   }\r
9401 \r
9402   /* Bind stderr local socket to unused "privileged" port address\r
9403    */\r
9404   s2 = INVALID_SOCKET;\r
9405   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9406   mysa.sin_family = AF_INET;\r
9407   mysa.sin_addr.s_addr = INADDR_ANY;\r
9408   for (fromPort = 1023;; fromPort--) {\r
9409     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9410     if (fromPort < 0) {\r
9411       (void) closesocket(s);\r
9412       WSACleanup();\r
9413       return WSAEADDRINUSE;\r
9414     }\r
9415     if (s2 == INVALID_SOCKET) {\r
9416       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9417         err = WSAGetLastError();\r
9418         closesocket(s);\r
9419         WSACleanup();\r
9420         return err;\r
9421       }\r
9422     }\r
9423     uport = (unsigned short) fromPort;\r
9424     mysa.sin_port = htons(uport);\r
9425     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9426         == SOCKET_ERROR) {\r
9427       err = WSAGetLastError();\r
9428       if (err == WSAEADDRINUSE) continue;\r
9429       (void) closesocket(s);\r
9430       WSACleanup();\r
9431       return err;\r
9432     }\r
9433     if (listen(s2, 1) == SOCKET_ERROR) {\r
9434       err = WSAGetLastError();\r
9435       if (err == WSAEADDRINUSE) {\r
9436         closesocket(s2);\r
9437         s2 = INVALID_SOCKET;\r
9438         continue;\r
9439       }\r
9440       (void) closesocket(s);\r
9441       (void) closesocket(s2);\r
9442       WSACleanup();\r
9443       return err;\r
9444     }\r
9445     break;\r
9446   }\r
9447   prevStderrPort = fromPort; // remember port used\r
9448   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9449 \r
9450   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9451     err = WSAGetLastError();\r
9452     (void) closesocket(s);\r
9453     (void) closesocket(s2);\r
9454     WSACleanup();\r
9455     return err;\r
9456   }\r
9457 \r
9458   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9459     err = WSAGetLastError();\r
9460     (void) closesocket(s);\r
9461     (void) closesocket(s2);\r
9462     WSACleanup();\r
9463     return err;\r
9464   }\r
9465   if (*user == NULLCHAR) user = UserName();\r
9466   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9467     err = WSAGetLastError();\r
9468     (void) closesocket(s);\r
9469     (void) closesocket(s2);\r
9470     WSACleanup();\r
9471     return err;\r
9472   }\r
9473   if (send(s, cmd, strlen(cmd) + 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 \r
9481   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\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   (void) closesocket(s2);  /* Stop listening */\r
9489 \r
9490   /* Prepare return value */\r
9491   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9492   cp->kind = CPRcmd;\r
9493   cp->sock = s;\r
9494   cp->sock2 = s3;\r
9495   *pr = (ProcRef *) cp;\r
9496 \r
9497   return NO_ERROR;\r
9498 }\r
9499 \r
9500 \r
9501 InputSourceRef\r
9502 AddInputSource(ProcRef pr, int lineByLine,\r
9503                InputCallback func, VOIDSTAR closure)\r
9504 {\r
9505   InputSource *is, *is2 = NULL;\r
9506   ChildProc *cp = (ChildProc *) pr;\r
9507 \r
9508   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9509   is->lineByLine = lineByLine;\r
9510   is->func = func;\r
9511   is->closure = closure;\r
9512   is->second = NULL;\r
9513   is->next = is->buf;\r
9514   if (pr == NoProc) {\r
9515     is->kind = CPReal;\r
9516     consoleInputSource = is;\r
9517   } else {\r
9518     is->kind = cp->kind;\r
9519     /* \r
9520         [AS] Try to avoid a race condition if the thread is given control too early:\r
9521         we create all threads suspended so that the is->hThread variable can be\r
9522         safely assigned, then let the threads start with ResumeThread.\r
9523     */\r
9524     switch (cp->kind) {\r
9525     case CPReal:\r
9526       is->hFile = cp->hFrom;\r
9527       cp->hFrom = NULL; /* now owned by InputThread */\r
9528       is->hThread =\r
9529         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9530                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9531       break;\r
9532 \r
9533     case CPComm:\r
9534       is->hFile = cp->hFrom;\r
9535       cp->hFrom = NULL; /* now owned by InputThread */\r
9536       is->hThread =\r
9537         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9538                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9539       break;\r
9540 \r
9541     case CPSock:\r
9542       is->sock = cp->sock;\r
9543       is->hThread =\r
9544         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9545                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9546       break;\r
9547 \r
9548     case CPRcmd:\r
9549       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9550       *is2 = *is;\r
9551       is->sock = cp->sock;\r
9552       is->second = is2;\r
9553       is2->sock = cp->sock2;\r
9554       is2->second = is2;\r
9555       is->hThread =\r
9556         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9557                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9558       is2->hThread =\r
9559         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9560                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9561       break;\r
9562     }\r
9563 \r
9564     if( is->hThread != NULL ) {\r
9565         ResumeThread( is->hThread );\r
9566     }\r
9567 \r
9568     if( is2 != NULL && is2->hThread != NULL ) {\r
9569         ResumeThread( is2->hThread );\r
9570     }\r
9571   }\r
9572 \r
9573   return (InputSourceRef) is;\r
9574 }\r
9575 \r
9576 void\r
9577 RemoveInputSource(InputSourceRef isr)\r
9578 {\r
9579   InputSource *is;\r
9580 \r
9581   is = (InputSource *) isr;\r
9582   is->hThread = NULL;  /* tell thread to stop */\r
9583   CloseHandle(is->hThread);\r
9584   if (is->second != NULL) {\r
9585     is->second->hThread = NULL;\r
9586     CloseHandle(is->second->hThread);\r
9587   }\r
9588 }\r
9589 \r
9590 int no_wrap(char *message, int count)\r
9591 {\r
9592     ConsoleOutput(message, count, FALSE);\r
9593     return count;\r
9594 }\r
9595 \r
9596 int\r
9597 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9598 {\r
9599   DWORD dOutCount;\r
9600   int outCount = SOCKET_ERROR;\r
9601   ChildProc *cp = (ChildProc *) pr;\r
9602   static OVERLAPPED ovl;\r
9603   static int line = 0;\r
9604 \r
9605   if (pr == NoProc)\r
9606   {\r
9607     if (appData.noJoin || !appData.useInternalWrap)\r
9608       return no_wrap(message, count);\r
9609     else\r
9610     {\r
9611       int width = get_term_width();\r
9612       int len = wrap(NULL, message, count, width, &line);\r
9613       char *msg = malloc(len);\r
9614       int dbgchk;\r
9615 \r
9616       if (!msg)\r
9617         return no_wrap(message, count);\r
9618       else\r
9619       {\r
9620         dbgchk = wrap(msg, message, count, width, &line);\r
9621         if (dbgchk != len && appData.debugMode)\r
9622             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9623         ConsoleOutput(msg, len, FALSE);\r
9624         free(msg);\r
9625         return len;\r
9626       }\r
9627     }\r
9628   }\r
9629 \r
9630   if (ovl.hEvent == NULL) {\r
9631     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9632   }\r
9633   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9634 \r
9635   switch (cp->kind) {\r
9636   case CPSock:\r
9637   case CPRcmd:\r
9638     outCount = send(cp->sock, message, count, 0);\r
9639     if (outCount == SOCKET_ERROR) {\r
9640       *outError = WSAGetLastError();\r
9641     } else {\r
9642       *outError = NO_ERROR;\r
9643     }\r
9644     break;\r
9645 \r
9646   case CPReal:\r
9647     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9648                   &dOutCount, NULL)) {\r
9649       *outError = NO_ERROR;\r
9650       outCount = (int) dOutCount;\r
9651     } else {\r
9652       *outError = GetLastError();\r
9653     }\r
9654     break;\r
9655 \r
9656   case CPComm:\r
9657     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9658                             &dOutCount, &ovl);\r
9659     if (*outError == NO_ERROR) {\r
9660       outCount = (int) dOutCount;\r
9661     }\r
9662     break;\r
9663   }\r
9664   return outCount;\r
9665 }\r
9666 \r
9667 void\r
9668 DoSleep(int n)\r
9669 {\r
9670     if(n != 0) Sleep(n);\r
9671 }\r
9672 \r
9673 int\r
9674 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9675                        long msdelay)\r
9676 {\r
9677   /* Ignore delay, not implemented for WinBoard */\r
9678   return OutputToProcess(pr, message, count, outError);\r
9679 }\r
9680 \r
9681 \r
9682 void\r
9683 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9684                         char *buf, int count, int error)\r
9685 {\r
9686   DisplayFatalError(_("Not implemented"), 0, 1);\r
9687 }\r
9688 \r
9689 /* see wgamelist.c for Game List functions */\r
9690 /* see wedittags.c for Edit Tags functions */\r
9691 \r
9692 \r
9693 VOID\r
9694 ICSInitScript()\r
9695 {\r
9696   FILE *f;\r
9697   char buf[MSG_SIZ];\r
9698   char *dummy;\r
9699 \r
9700   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9701     f = fopen(buf, "r");\r
9702     if (f != NULL) {\r
9703       ProcessICSInitScript(f);\r
9704       fclose(f);\r
9705     }\r
9706   }\r
9707 }\r
9708 \r
9709 \r
9710 VOID\r
9711 StartAnalysisClock()\r
9712 {\r
9713   if (analysisTimerEvent) return;\r
9714   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9715                                         (UINT) 2000, NULL);\r
9716 }\r
9717 \r
9718 VOID\r
9719 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9720 {\r
9721   highlightInfo.sq[0].x = fromX;\r
9722   highlightInfo.sq[0].y = fromY;\r
9723   highlightInfo.sq[1].x = toX;\r
9724   highlightInfo.sq[1].y = toY;\r
9725 }\r
9726 \r
9727 VOID\r
9728 ClearHighlights()\r
9729 {\r
9730   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9731     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9732 }\r
9733 \r
9734 VOID\r
9735 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9736 {\r
9737   premoveHighlightInfo.sq[0].x = fromX;\r
9738   premoveHighlightInfo.sq[0].y = fromY;\r
9739   premoveHighlightInfo.sq[1].x = toX;\r
9740   premoveHighlightInfo.sq[1].y = toY;\r
9741 }\r
9742 \r
9743 VOID\r
9744 ClearPremoveHighlights()\r
9745 {\r
9746   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9747     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9748 }\r
9749 \r
9750 VOID\r
9751 ShutDownFrontEnd()\r
9752 {\r
9753   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9754   DeleteClipboardTempFiles();\r
9755 }\r
9756 \r
9757 void\r
9758 BoardToTop()\r
9759 {\r
9760     if (IsIconic(hwndMain))\r
9761       ShowWindow(hwndMain, SW_RESTORE);\r
9762 \r
9763     SetActiveWindow(hwndMain);\r
9764 }\r
9765 \r
9766 /*\r
9767  * Prototypes for animation support routines\r
9768  */\r
9769 static void ScreenSquare(int column, int row, POINT * pt);\r
9770 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9771      POINT frames[], int * nFrames);\r
9772 \r
9773 \r
9774 #define kFactor 4\r
9775 \r
9776 void\r
9777 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9778 {       // [HGM] atomic: animate blast wave\r
9779         int i;\r
9780 \r
9781         explodeInfo.fromX = fromX;\r
9782         explodeInfo.fromY = fromY;\r
9783         explodeInfo.toX = toX;\r
9784         explodeInfo.toY = toY;\r
9785         for(i=1; i<4*kFactor; i++) {\r
9786             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9787             DrawPosition(FALSE, board);\r
9788             Sleep(appData.animSpeed);\r
9789         }\r
9790         explodeInfo.radius = 0;\r
9791         DrawPosition(TRUE, board);\r
9792 }\r
9793 \r
9794 void\r
9795 AnimateMove(board, fromX, fromY, toX, toY)\r
9796      Board board;\r
9797      int fromX;\r
9798      int fromY;\r
9799      int toX;\r
9800      int toY;\r
9801 {\r
9802   ChessSquare piece;\r
9803   POINT start, finish, mid;\r
9804   POINT frames[kFactor * 2 + 1];\r
9805   int nFrames, n;\r
9806 \r
9807   if (!appData.animate) return;\r
9808   if (doingSizing) return;\r
9809   if (fromY < 0 || fromX < 0) return;\r
9810   piece = board[fromY][fromX];\r
9811   if (piece >= EmptySquare) return;\r
9812 \r
9813   ScreenSquare(fromX, fromY, &start);\r
9814   ScreenSquare(toX, toY, &finish);\r
9815 \r
9816   /* All moves except knight jumps move in straight line */\r
9817   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9818     mid.x = start.x + (finish.x - start.x) / 2;\r
9819     mid.y = start.y + (finish.y - start.y) / 2;\r
9820   } else {\r
9821     /* Knight: make straight movement then diagonal */\r
9822     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9823        mid.x = start.x + (finish.x - start.x) / 2;\r
9824        mid.y = start.y;\r
9825      } else {\r
9826        mid.x = start.x;\r
9827        mid.y = start.y + (finish.y - start.y) / 2;\r
9828      }\r
9829   }\r
9830   \r
9831   /* Don't use as many frames for very short moves */\r
9832   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9833     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9834   else\r
9835     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9836 \r
9837   animInfo.from.x = fromX;\r
9838   animInfo.from.y = fromY;\r
9839   animInfo.to.x = toX;\r
9840   animInfo.to.y = toY;\r
9841   animInfo.lastpos = start;\r
9842   animInfo.piece = piece;\r
9843   for (n = 0; n < nFrames; n++) {\r
9844     animInfo.pos = frames[n];\r
9845     DrawPosition(FALSE, NULL);\r
9846     animInfo.lastpos = animInfo.pos;\r
9847     Sleep(appData.animSpeed);\r
9848   }\r
9849   animInfo.pos = finish;\r
9850   DrawPosition(FALSE, NULL);\r
9851   animInfo.piece = EmptySquare;\r
9852   Explode(board, fromX, fromY, toX, toY);\r
9853 }\r
9854 \r
9855 /*      Convert board position to corner of screen rect and color       */\r
9856 \r
9857 static void\r
9858 ScreenSquare(column, row, pt)\r
9859      int column; int row; POINT * pt;\r
9860 {\r
9861   if (flipView) {\r
9862     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9863     pt->y = lineGap + row * (squareSize + lineGap);\r
9864   } else {\r
9865     pt->x = lineGap + column * (squareSize + lineGap);\r
9866     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9867   }\r
9868 }\r
9869 \r
9870 /*      Generate a series of frame coords from start->mid->finish.\r
9871         The movement rate doubles until the half way point is\r
9872         reached, then halves back down to the final destination,\r
9873         which gives a nice slow in/out effect. The algorithmn\r
9874         may seem to generate too many intermediates for short\r
9875         moves, but remember that the purpose is to attract the\r
9876         viewers attention to the piece about to be moved and\r
9877         then to where it ends up. Too few frames would be less\r
9878         noticeable.                                             */\r
9879 \r
9880 static void\r
9881 Tween(start, mid, finish, factor, frames, nFrames)\r
9882      POINT * start; POINT * mid;\r
9883      POINT * finish; int factor;\r
9884      POINT frames[]; int * nFrames;\r
9885 {\r
9886   int n, fraction = 1, count = 0;\r
9887 \r
9888   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9889   for (n = 0; n < factor; n++)\r
9890     fraction *= 2;\r
9891   for (n = 0; n < factor; n++) {\r
9892     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9893     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9894     count ++;\r
9895     fraction = fraction / 2;\r
9896   }\r
9897   \r
9898   /* Midpoint */\r
9899   frames[count] = *mid;\r
9900   count ++;\r
9901   \r
9902   /* Slow out, stepping 1/2, then 1/4, ... */\r
9903   fraction = 2;\r
9904   for (n = 0; n < factor; n++) {\r
9905     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9906     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9907     count ++;\r
9908     fraction = fraction * 2;\r
9909   }\r
9910   *nFrames = count;\r
9911 }\r
9912 \r
9913 void\r
9914 SettingsPopUp(ChessProgramState *cps)\r
9915 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9916       EngineOptionsPopup(savedHwnd, cps);\r
9917 }\r
9918 \r
9919 int flock(int fid, int code)\r
9920 {\r
9921     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9922     OVERLAPPED ov;\r
9923     ov.hEvent = NULL;\r
9924     ov.Offset = 0;\r
9925     ov.OffsetHigh = 0;\r
9926     switch(code) {\r
9927       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9928       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9929       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9930       default: return -1;\r
9931     }\r
9932     return 0;\r
9933 }\r