6f26f2b7376d4bf6a370c583d592fab472f044ea
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][42] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
257   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
259   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
262   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
265   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
266 { ABOUTBOX2, IDC_ChessBoard }, \r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
268   OPT_GameListClose, IDC_GameListDoFilter }, \r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
270 { DLG_Error, IDOK }, \r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
272   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
275   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
276   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
277 { DLG_IndexNumber, IDC_Index }, \r
278 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
279 { DLG_TypeInName, IDOK, IDCANCEL }, \r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
281   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
283   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
284   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
285   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
286   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
287   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
288   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
290   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
291   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
292   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
293   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
294   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
295   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
296   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
297   GPB_General, GPB_Alarm }, \r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
299   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
300   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
301   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
302   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
303   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
304   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
305   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
307   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
308   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
309   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
310   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
311   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
312   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
313   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
314   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
316   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
317   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
318   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
319   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
322   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
323 { DLG_MoveHistory }, \r
324 { DLG_EvalGraph }, \r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
328   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
329   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
330   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
332   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
333   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
334 { 0 }\r
335 };\r
336 \r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
338 static int lastChecked;\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
340 extern int tinyLayout;\r
341 extern char * menuBarText[][10];\r
342 \r
343 void\r
344 LoadLanguageFile(char *name)\r
345 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
346     FILE *f;\r
347     int i=0, j=0, n=0, k;\r
348     char buf[MSG_SIZ];\r
349 \r
350     if(!name || name[0] == NULLCHAR) return;\r
351       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
352     appData.language = oldLanguage;\r
353     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
354     if((f = fopen(buf, "r")) == NULL) return;\r
355     while((k = fgetc(f)) != EOF) {\r
356         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
357         languageBuf[i] = k;\r
358         if(k == '\n') {\r
359             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
360                 char *p;\r
361                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
362                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
363                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
364                         english[j] = languageBuf + n + 1; *p = 0;\r
365                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
367                     }\r
368                 }\r
369             }\r
370             n = i + 1;\r
371         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
372             switch(k) {\r
373               case 'n': k = '\n'; break;\r
374               case 'r': k = '\r'; break;\r
375               case 't': k = '\t'; break;\r
376             }\r
377             languageBuf[--i] = k;\r
378         }\r
379         i++;\r
380     }\r
381     fclose(f);\r
382     barbaric = (j != 0);\r
383     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
384 }\r
385 \r
386 char *\r
387 T_(char *s)\r
388 {   // return the translation of the given string\r
389     // efficiency can be improved a lot...\r
390     int i=0;\r
391     static char buf[MSG_SIZ];\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
393     if(!barbaric) return s;\r
394     if(!s) return ""; // sanity\r
395     while(english[i]) {\r
396         if(!strcmp(s, english[i])) return foreign[i];\r
397         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
398             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
399             return buf;\r
400         }\r
401         i++;\r
402     }\r
403     return s;\r
404 }\r
405 \r
406 void\r
407 Translate(HWND hDlg, int dialogID)\r
408 {   // translate all text items in the given dialog\r
409     int i=0, j, k;\r
410     char buf[MSG_SIZ], *s;\r
411     if(!barbaric) return;\r
412     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
413     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
414     GetWindowText( hDlg, buf, MSG_SIZ );\r
415     s = T_(buf);\r
416     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
417     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
418         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
419         if(strlen(buf) == 0) continue;\r
420         s = T_(buf);\r
421         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
422     }\r
423 }\r
424 \r
425 HMENU\r
426 TranslateOneMenu(int i, HMENU subMenu)\r
427 {\r
428     int j;\r
429     static MENUITEMINFO info;\r
430 \r
431     info.cbSize = sizeof(MENUITEMINFO);\r
432     info.fMask = MIIM_STATE | MIIM_TYPE;\r
433           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
434             char buf[MSG_SIZ];\r
435             info.dwTypeData = buf;\r
436             info.cch = sizeof(buf);\r
437             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
438             if(i < 10) {
439                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
440                 else menuText[i][j] = strdup(buf); // remember original on first change\r
441             }\r
442             if(buf[0] == NULLCHAR) continue;\r
443             info.dwTypeData = T_(buf);\r
444             info.cch = strlen(buf)+1;\r
445             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
446           }\r
447     return subMenu;\r
448 }\r
449 \r
450 void\r
451 TranslateMenus(int addLanguage)\r
452 {\r
453     int i;\r
454     WIN32_FIND_DATA fileData;\r
455     HANDLE hFind;\r
456 #define IDM_English 1970\r
457     if(1) {\r
458         HMENU mainMenu = GetMenu(hwndMain);\r
459         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
460           HMENU subMenu = GetSubMenu(mainMenu, i);\r
461           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
462                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
463           TranslateOneMenu(i, subMenu);\r
464         }\r
465         DrawMenuBar(hwndMain);\r
466     }\r
467 \r
468     if(!addLanguage) return;\r
469     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
470         HMENU mainMenu = GetMenu(hwndMain);\r
471         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
472         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
473         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
474         i = 0; lastChecked = IDM_English;\r
475         do {\r
476             char *p, *q = fileData.cFileName;\r
477             int checkFlag = MF_UNCHECKED;\r
478             languageFile[i] = strdup(q);\r
479             if(barbaric && !strcmp(oldLanguage, q)) {\r
480                 checkFlag = MF_CHECKED;\r
481                 lastChecked = IDM_English + i + 1;\r
482                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
483             }\r
484             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
485             p = strstr(fileData.cFileName, ".lng");\r
486             if(p) *p = 0;\r
487             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
488         } while(FindNextFile(hFind, &fileData));\r
489         FindClose(hFind);\r
490     }\r
491 }\r
492 \r
493 #endif\r
494 \r
495 typedef struct {\r
496   char *name;\r
497   int squareSize;\r
498   int lineGap;\r
499   int smallLayout;\r
500   int tinyLayout;\r
501   int cliWidth, cliHeight;\r
502 } SizeInfo;\r
503 \r
504 SizeInfo sizeInfo[] = \r
505 {\r
506   { "tiny",     21, 0, 1, 1, 0, 0 },\r
507   { "teeny",    25, 1, 1, 1, 0, 0 },\r
508   { "dinky",    29, 1, 1, 1, 0, 0 },\r
509   { "petite",   33, 1, 1, 1, 0, 0 },\r
510   { "slim",     37, 2, 1, 0, 0, 0 },\r
511   { "small",    40, 2, 1, 0, 0, 0 },\r
512   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
513   { "middling", 49, 2, 0, 0, 0, 0 },\r
514   { "average",  54, 2, 0, 0, 0, 0 },\r
515   { "moderate", 58, 3, 0, 0, 0, 0 },\r
516   { "medium",   64, 3, 0, 0, 0, 0 },\r
517   { "bulky",    72, 3, 0, 0, 0, 0 },\r
518   { "large",    80, 3, 0, 0, 0, 0 },\r
519   { "big",      87, 3, 0, 0, 0, 0 },\r
520   { "huge",     95, 3, 0, 0, 0, 0 },\r
521   { "giant",    108, 3, 0, 0, 0, 0 },\r
522   { "colossal", 116, 4, 0, 0, 0, 0 },\r
523   { "titanic",  129, 4, 0, 0, 0, 0 },\r
524   { NULL, 0, 0, 0, 0, 0, 0 }\r
525 };\r
526 \r
527 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
528 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
529 {\r
530   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
531   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
532   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
533   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
534   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
535   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
536   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
537   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
538   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
539   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
540   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
541   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
542   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
543   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
544   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
545   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
546   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
547   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
548 };\r
549 \r
550 MyFont *font[NUM_SIZES][NUM_FONTS];\r
551 \r
552 typedef struct {\r
553   char *label;\r
554   int id;\r
555   HWND hwnd;\r
556   WNDPROC wndproc;\r
557 } MyButtonDesc;\r
558 \r
559 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
560 #define N_BUTTONS 5\r
561 \r
562 MyButtonDesc buttonDesc[N_BUTTONS] =\r
563 {\r
564   {"<<", IDM_ToStart, NULL, NULL},\r
565   {"<", IDM_Backward, NULL, NULL},\r
566   {"P", IDM_Pause, NULL, NULL},\r
567   {">", IDM_Forward, NULL, NULL},\r
568   {">>", IDM_ToEnd, NULL, NULL},\r
569 };\r
570 \r
571 int tinyLayout = 0, smallLayout = 0;\r
572 #define MENU_BAR_ITEMS 9\r
573 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
574   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
575   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
576 };\r
577 \r
578 \r
579 MySound sounds[(int)NSoundClasses];\r
580 MyTextAttribs textAttribs[(int)NColorClasses];\r
581 \r
582 MyColorizeAttribs colorizeAttribs[] = {\r
583   { (COLORREF)0, 0, N_("Shout Text") },\r
584   { (COLORREF)0, 0, N_("SShout/CShout") },\r
585   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
586   { (COLORREF)0, 0, N_("Channel Text") },\r
587   { (COLORREF)0, 0, N_("Kibitz Text") },\r
588   { (COLORREF)0, 0, N_("Tell Text") },\r
589   { (COLORREF)0, 0, N_("Challenge Text") },\r
590   { (COLORREF)0, 0, N_("Request Text") },\r
591   { (COLORREF)0, 0, N_("Seek Text") },\r
592   { (COLORREF)0, 0, N_("Normal Text") },\r
593   { (COLORREF)0, 0, N_("None") }\r
594 };\r
595 \r
596 \r
597 \r
598 static char *commentTitle;\r
599 static char *commentText;\r
600 static int commentIndex;\r
601 static Boolean editComment = FALSE;\r
602 \r
603 \r
604 char errorTitle[MSG_SIZ];\r
605 char errorMessage[2*MSG_SIZ];\r
606 HWND errorDialog = NULL;\r
607 BOOLEAN moveErrorMessageUp = FALSE;\r
608 BOOLEAN consoleEcho = TRUE;\r
609 CHARFORMAT consoleCF;\r
610 COLORREF consoleBackgroundColor;\r
611 \r
612 char *programVersion;\r
613 \r
614 #define CPReal 1\r
615 #define CPComm 2\r
616 #define CPSock 3\r
617 #define CPRcmd 4\r
618 typedef int CPKind;\r
619 \r
620 typedef struct {\r
621   CPKind kind;\r
622   HANDLE hProcess;\r
623   DWORD pid;\r
624   HANDLE hTo;\r
625   HANDLE hFrom;\r
626   SOCKET sock;\r
627   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
628 } ChildProc;\r
629 \r
630 #define INPUT_SOURCE_BUF_SIZE 4096\r
631 \r
632 typedef struct _InputSource {\r
633   CPKind kind;\r
634   HANDLE hFile;\r
635   SOCKET sock;\r
636   int lineByLine;\r
637   HANDLE hThread;\r
638   DWORD id;\r
639   char buf[INPUT_SOURCE_BUF_SIZE];\r
640   char *next;\r
641   DWORD count;\r
642   int error;\r
643   InputCallback func;\r
644   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
645   VOIDSTAR closure;\r
646 } InputSource;\r
647 \r
648 InputSource *consoleInputSource;\r
649 \r
650 DCB dcb;\r
651 \r
652 /* forward */\r
653 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
654 VOID ConsoleCreate();\r
655 LRESULT CALLBACK\r
656   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
657 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
658 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
659 VOID ParseCommSettings(char *arg, DCB *dcb);\r
660 LRESULT CALLBACK\r
661   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
662 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
663 void ParseIcsTextMenu(char *icsTextMenuString);\r
664 VOID PopUpNameDialog(char firstchar);\r
665 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
666 \r
667 /* [AS] */\r
668 int NewGameFRC();\r
669 int GameListOptions();\r
670 \r
671 int dummy; // [HGM] for obsolete args\r
672 \r
673 HWND hwndMain = NULL;        /* root window*/\r
674 HWND hwndConsole = NULL;\r
675 HWND commentDialog = NULL;\r
676 HWND moveHistoryDialog = NULL;\r
677 HWND evalGraphDialog = NULL;\r
678 HWND engineOutputDialog = NULL;\r
679 HWND gameListDialog = NULL;\r
680 HWND editTagsDialog = NULL;\r
681 \r
682 int commentUp = FALSE;\r
683 \r
684 WindowPlacement wpMain;\r
685 WindowPlacement wpConsole;\r
686 WindowPlacement wpComment;\r
687 WindowPlacement wpMoveHistory;\r
688 WindowPlacement wpEvalGraph;\r
689 WindowPlacement wpEngineOutput;\r
690 WindowPlacement wpGameList;\r
691 WindowPlacement wpTags;\r
692 \r
693 VOID EngineOptionsPopup(); // [HGM] settings\r
694 \r
695 VOID GothicPopUp(char *title, VariantClass variant);\r
696 /*\r
697  * Setting "frozen" should disable all user input other than deleting\r
698  * the window.  We do this while engines are initializing themselves.\r
699  */\r
700 static int frozen = 0;\r
701 static int oldMenuItemState[MENU_BAR_ITEMS];\r
702 void FreezeUI()\r
703 {\r
704   HMENU hmenu;\r
705   int i;\r
706 \r
707   if (frozen) return;\r
708   frozen = 1;\r
709   hmenu = GetMenu(hwndMain);\r
710   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
711     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
712   }\r
713   DrawMenuBar(hwndMain);\r
714 }\r
715 \r
716 /* Undo a FreezeUI */\r
717 void ThawUI()\r
718 {\r
719   HMENU hmenu;\r
720   int i;\r
721 \r
722   if (!frozen) return;\r
723   frozen = 0;\r
724   hmenu = GetMenu(hwndMain);\r
725   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
726     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
727   }\r
728   DrawMenuBar(hwndMain);\r
729 }\r
730 \r
731 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
732 \r
733 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
734 #ifdef JAWS\r
735 #include "jaws.c"\r
736 #else\r
737 #define JAWS_INIT\r
738 #define JAWS_ARGS\r
739 #define JAWS_ALT_INTERCEPT\r
740 #define JAWS_KBUP_NAVIGATION\r
741 #define JAWS_KBDOWN_NAVIGATION\r
742 #define JAWS_MENU_ITEMS\r
743 #define JAWS_SILENCE\r
744 #define JAWS_REPLAY\r
745 #define JAWS_ACCEL\r
746 #define JAWS_COPYRIGHT\r
747 #define JAWS_DELETE(X) X\r
748 #define SAYMACHINEMOVE()\r
749 #define SAY(X)\r
750 #endif\r
751 \r
752 /*---------------------------------------------------------------------------*\\r
753  *\r
754  * WinMain\r
755  *\r
756 \*---------------------------------------------------------------------------*/\r
757 \r
758 int APIENTRY\r
759 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
760         LPSTR lpCmdLine, int nCmdShow)\r
761 {\r
762   MSG msg;\r
763   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
764 //  INITCOMMONCONTROLSEX ex;\r
765 \r
766   debugFP = stderr;\r
767 \r
768   LoadLibrary("RICHED32.DLL");\r
769   consoleCF.cbSize = sizeof(CHARFORMAT);\r
770 \r
771   if (!InitApplication(hInstance)) {\r
772     return (FALSE);\r
773   }\r
774   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
775     return (FALSE);\r
776   }\r
777 \r
778   JAWS_INIT\r
779   TranslateMenus(1);\r
780 \r
781 //  InitCommonControlsEx(&ex);\r
782   InitCommonControls();\r
783 \r
784   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
785   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
786   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
787 \r
788   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
789 \r
790   while (GetMessage(&msg, /* message structure */\r
791                     NULL, /* handle of window receiving the message */\r
792                     0,    /* lowest message to examine */\r
793                     0))   /* highest message to examine */\r
794     {\r
795 \r
796       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
797         // [HGM] navigate: switch between all windows with tab\r
798         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
799         int i, currentElement = 0;\r
800 \r
801         // first determine what element of the chain we come from (if any)\r
802         if(appData.icsActive) {\r
803             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
804             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
805         }\r
806         if(engineOutputDialog && EngineOutputIsUp()) {\r
807             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
808             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
809         }\r
810         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
811             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
812         }\r
813         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
814         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
815         if(msg.hwnd == e1)                 currentElement = 2; else\r
816         if(msg.hwnd == e2)                 currentElement = 3; else\r
817         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
818         if(msg.hwnd == mh)                currentElement = 4; else\r
819         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
820         if(msg.hwnd == hText)  currentElement = 5; else\r
821         if(msg.hwnd == hInput) currentElement = 6; else\r
822         for (i = 0; i < N_BUTTONS; i++) {\r
823             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
824         }\r
825 \r
826         // determine where to go to\r
827         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
828           do {\r
829             currentElement = (currentElement + direction) % 7;\r
830             switch(currentElement) {\r
831                 case 0:\r
832                   h = hwndMain; break; // passing this case always makes the loop exit\r
833                 case 1:\r
834                   h = buttonDesc[0].hwnd; break; // could be NULL\r
835                 case 2:\r
836                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
837                   h = e1; break;\r
838                 case 3:\r
839                   if(!EngineOutputIsUp()) continue;\r
840                   h = e2; break;\r
841                 case 4:\r
842                   if(!MoveHistoryIsUp()) continue;\r
843                   h = mh; break;\r
844 //              case 6: // input to eval graph does not seem to get here!\r
845 //                if(!EvalGraphIsUp()) continue;\r
846 //                h = evalGraphDialog; break;\r
847                 case 5:\r
848                   if(!appData.icsActive) continue;\r
849                   SAY("display");\r
850                   h = hText; break;\r
851                 case 6:\r
852                   if(!appData.icsActive) continue;\r
853                   SAY("input");\r
854                   h = hInput; break;\r
855             }\r
856           } while(h == 0);\r
857 \r
858           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
859           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
860           SetFocus(h);\r
861 \r
862           continue; // this message now has been processed\r
863         }\r
864       }\r
865 \r
866       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
867           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
868           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
869           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
870           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
871           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
872           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
873           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
874           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
875           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
876         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
877         for(i=0; i<MAX_CHAT; i++) \r
878             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
879                 done = 1; break;\r
880         }\r
881         if(done) continue; // [HGM] chat: end patch\r
882         TranslateMessage(&msg); /* Translates virtual key codes */\r
883         DispatchMessage(&msg);  /* Dispatches message to window */\r
884       }\r
885     }\r
886 \r
887 \r
888   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
889 }\r
890 \r
891 /*---------------------------------------------------------------------------*\\r
892  *\r
893  * Initialization functions\r
894  *\r
895 \*---------------------------------------------------------------------------*/\r
896 \r
897 void\r
898 SetUserLogo()\r
899 {   // update user logo if necessary\r
900     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
901 \r
902     if(appData.autoLogo) {\r
903           curName = UserName();\r
904           if(strcmp(curName, oldUserName)) {\r
905                 GetCurrentDirectory(MSG_SIZ, dir);\r
906                 SetCurrentDirectory(installDir);\r
907                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
908                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
909                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
910                 if(userLogo == NULL)\r
911                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
912                 SetCurrentDirectory(dir); /* return to prev directory */\r
913           }\r
914     }\r
915 }\r
916 \r
917 BOOL\r
918 InitApplication(HINSTANCE hInstance)\r
919 {\r
920   WNDCLASS wc;\r
921 \r
922   /* Fill in window class structure with parameters that describe the */\r
923   /* main window. */\r
924 \r
925   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
926   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
927   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
928   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
929   wc.hInstance     = hInstance;         /* Owner of this class */\r
930   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
931   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
932   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
933   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
934   wc.lpszClassName = szAppName;                 /* Name to register as */\r
935 \r
936   /* Register the window class and return success/failure code. */\r
937   if (!RegisterClass(&wc)) return FALSE;\r
938 \r
939   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
940   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
941   wc.cbClsExtra    = 0;\r
942   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
943   wc.hInstance     = hInstance;\r
944   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
945   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
946   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
947   wc.lpszMenuName  = NULL;\r
948   wc.lpszClassName = szConsoleName;\r
949 \r
950   if (!RegisterClass(&wc)) return FALSE;\r
951   return TRUE;\r
952 }\r
953 \r
954 \r
955 /* Set by InitInstance, used by EnsureOnScreen */\r
956 int screenHeight, screenWidth;\r
957 \r
958 void\r
959 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
960 {\r
961 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
962   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
963   if (*x > screenWidth - 32) *x = 0;\r
964   if (*y > screenHeight - 32) *y = 0;\r
965   if (*x < minX) *x = minX;\r
966   if (*y < minY) *y = minY;\r
967 }\r
968 \r
969 VOID\r
970 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
971 {\r
972   char buf[MSG_SIZ], dir[MSG_SIZ];\r
973   GetCurrentDirectory(MSG_SIZ, dir);\r
974   SetCurrentDirectory(installDir);\r
975   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
976       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
977 \r
978       if (cps->programLogo == NULL && appData.debugMode) {\r
979           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
980       }\r
981   } else if(appData.autoLogo) {\r
982       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
983         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
984         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
985       } else\r
986       if(appData.directory[n] && appData.directory[n][0]) {\r
987         SetCurrentDirectory(appData.directory[n]);\r
988         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
989       }\r
990   }\r
991   SetCurrentDirectory(dir); /* return to prev directory */\r
992 }\r
993 \r
994 VOID\r
995 InitTextures()\r
996 {\r
997   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
998   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
999   \r
1000   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1001       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1002       liteBackTextureMode = appData.liteBackTextureMode;\r
1003 \r
1004       if (liteBackTexture == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1006       }\r
1007   }\r
1008   \r
1009   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1010       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1011       darkBackTextureMode = appData.darkBackTextureMode;\r
1012 \r
1013       if (darkBackTexture == NULL && appData.debugMode) {\r
1014           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1015       }\r
1016   }\r
1017 }\r
1018 \r
1019 BOOL\r
1020 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1021 {\r
1022   HWND hwnd; /* Main window handle. */\r
1023   int ibs;\r
1024   WINDOWPLACEMENT wp;\r
1025   char *filepart;\r
1026 \r
1027   hInst = hInstance;    /* Store instance handle in our global variable */\r
1028   programName = szAppName;\r
1029 \r
1030   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1031     *filepart = NULLCHAR;\r
1032   } else {\r
1033     GetCurrentDirectory(MSG_SIZ, installDir);\r
1034   }\r
1035   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1036   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1037   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1038   /* xboard, and older WinBoards, controlled the move sound with the\r
1039      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1040      always turn the option on (so that the backend will call us),\r
1041      then let the user turn the sound off by setting it to silence if\r
1042      desired.  To accommodate old winboard.ini files saved by old\r
1043      versions of WinBoard, we also turn off the sound if the option\r
1044      was initially set to false. [HGM] taken out of InitAppData */\r
1045   if (!appData.ringBellAfterMoves) {\r
1046     sounds[(int)SoundMove].name = strdup("");\r
1047     appData.ringBellAfterMoves = TRUE;\r
1048   }\r
1049   if (appData.debugMode) {\r
1050     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1051     setbuf(debugFP, NULL);\r
1052   }\r
1053 \r
1054   LoadLanguageFile(appData.language);\r
1055 \r
1056   InitBackEnd1();\r
1057 \r
1058 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1059 //  InitEngineUCI( installDir, &second );\r
1060 \r
1061   /* Create a main window for this application instance. */\r
1062   hwnd = CreateWindow(szAppName, szTitle,\r
1063                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1064                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1065                       NULL, NULL, hInstance, NULL);\r
1066   hwndMain = hwnd;\r
1067 \r
1068   /* If window could not be created, return "failure" */\r
1069   if (!hwnd) {\r
1070     return (FALSE);\r
1071   }\r
1072 \r
1073   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1074   LoadLogo(&first, 0, FALSE);\r
1075   LoadLogo(&second, 1, appData.icsActive);\r
1076 \r
1077   SetUserLogo();\r
1078 \r
1079   iconWhite = LoadIcon(hInstance, "icon_white");\r
1080   iconBlack = LoadIcon(hInstance, "icon_black");\r
1081   iconCurrent = iconWhite;\r
1082   InitDrawingColors();\r
1083   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1084   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1085   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1086     /* Compute window size for each board size, and use the largest\r
1087        size that fits on this screen as the default. */\r
1088     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1089     if (boardSize == (BoardSize)-1 &&\r
1090         winH <= screenHeight\r
1091            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1092         && winW <= screenWidth) {\r
1093       boardSize = (BoardSize)ibs;\r
1094     }\r
1095   }\r
1096 \r
1097   InitDrawingSizes(boardSize, 0);\r
1098   InitMenuChecks();\r
1099   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1100 \r
1101   /* [AS] Load textures if specified */\r
1102   InitTextures();\r
1103 \r
1104   mysrandom( (unsigned) time(NULL) );\r
1105 \r
1106   /* [AS] Restore layout */\r
1107   if( wpMoveHistory.visible ) {\r
1108       MoveHistoryPopUp();\r
1109   }\r
1110 \r
1111   if( wpEvalGraph.visible ) {\r
1112       EvalGraphPopUp();\r
1113   }\r
1114 \r
1115   if( wpEngineOutput.visible ) {\r
1116       EngineOutputPopUp();\r
1117   }\r
1118 \r
1119   /* Make the window visible; update its client area; and return "success" */\r
1120   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1121   wp.length = sizeof(WINDOWPLACEMENT);\r
1122   wp.flags = 0;\r
1123   wp.showCmd = nCmdShow;\r
1124   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1125   wp.rcNormalPosition.left = wpMain.x;\r
1126   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1127   wp.rcNormalPosition.top = wpMain.y;\r
1128   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1129   SetWindowPlacement(hwndMain, &wp);\r
1130 \r
1131   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1132 \r
1133   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1134                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1135 \r
1136   if (hwndConsole) {\r
1137 #if AOT_CONSOLE\r
1138     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1139                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1140 #endif\r
1141     ShowWindow(hwndConsole, nCmdShow);\r
1142     SetActiveWindow(hwndConsole);\r
1143   }\r
1144   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1145   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1146 \r
1147   return TRUE;\r
1148 \r
1149 }\r
1150 \r
1151 VOID\r
1152 InitMenuChecks()\r
1153 {\r
1154   HMENU hmenu = GetMenu(hwndMain);\r
1155 \r
1156   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1157                         MF_BYCOMMAND|((appData.icsActive &&\r
1158                                        *appData.icsCommPort != NULLCHAR) ?\r
1159                                       MF_ENABLED : MF_GRAYED));\r
1160   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1161                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1162                                      MF_CHECKED : MF_UNCHECKED));\r
1163 }\r
1164 \r
1165 //---------------------------------------------------------------------------------------------------------\r
1166 \r
1167 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1168 #define XBOARD FALSE\r
1169 \r
1170 #define OPTCHAR "/"\r
1171 #define SEPCHAR "="\r
1172 \r
1173 #include "args.h"\r
1174 \r
1175 // front-end part of option handling\r
1176 \r
1177 VOID\r
1178 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1179 {\r
1180   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1181   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1182   DeleteDC(hdc);\r
1183   lf->lfWidth = 0;\r
1184   lf->lfEscapement = 0;\r
1185   lf->lfOrientation = 0;\r
1186   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1187   lf->lfItalic = mfp->italic;\r
1188   lf->lfUnderline = mfp->underline;\r
1189   lf->lfStrikeOut = mfp->strikeout;\r
1190   lf->lfCharSet = mfp->charset;\r
1191   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1192   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1193   lf->lfQuality = DEFAULT_QUALITY;\r
1194   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1195     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1196 }\r
1197 \r
1198 void\r
1199 CreateFontInMF(MyFont *mf)\r
1200\r
1201   LFfromMFP(&mf->lf, &mf->mfp);\r
1202   if (mf->hf) DeleteObject(mf->hf);\r
1203   mf->hf = CreateFontIndirect(&mf->lf);\r
1204 }\r
1205 \r
1206 // [HGM] This platform-dependent table provides the location for storing the color info\r
1207 void *\r
1208 colorVariable[] = {\r
1209   &whitePieceColor, \r
1210   &blackPieceColor, \r
1211   &lightSquareColor,\r
1212   &darkSquareColor, \r
1213   &highlightSquareColor,\r
1214   &premoveHighlightColor,\r
1215   NULL,\r
1216   &consoleBackgroundColor,\r
1217   &appData.fontForeColorWhite,\r
1218   &appData.fontBackColorWhite,\r
1219   &appData.fontForeColorBlack,\r
1220   &appData.fontBackColorBlack,\r
1221   &appData.evalHistColorWhite,\r
1222   &appData.evalHistColorBlack,\r
1223   &appData.highlightArrowColor,\r
1224 };\r
1225 \r
1226 /* Command line font name parser.  NULL name means do nothing.\r
1227    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1228    For backward compatibility, syntax without the colon is also\r
1229    accepted, but font names with digits in them won't work in that case.\r
1230 */\r
1231 VOID\r
1232 ParseFontName(char *name, MyFontParams *mfp)\r
1233 {\r
1234   char *p, *q;\r
1235   if (name == NULL) return;\r
1236   p = name;\r
1237   q = strchr(p, ':');\r
1238   if (q) {\r
1239     if (q - p >= sizeof(mfp->faceName))\r
1240       ExitArgError(_("Font name too long:"), name, TRUE);\r
1241     memcpy(mfp->faceName, p, q - p);\r
1242     mfp->faceName[q - p] = NULLCHAR;\r
1243     p = q + 1;\r
1244   } else {\r
1245     q = mfp->faceName;\r
1246     while (*p && !isdigit(*p)) {\r
1247       *q++ = *p++;\r
1248       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1249         ExitArgError(_("Font name too long:"), name, TRUE);\r
1250     }\r
1251     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1252     *q = NULLCHAR;\r
1253   }\r
1254   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1255   mfp->pointSize = (float) atof(p);\r
1256   mfp->bold = (strchr(p, 'b') != NULL);\r
1257   mfp->italic = (strchr(p, 'i') != NULL);\r
1258   mfp->underline = (strchr(p, 'u') != NULL);\r
1259   mfp->strikeout = (strchr(p, 's') != NULL);\r
1260   mfp->charset = DEFAULT_CHARSET;\r
1261   q = strchr(p, 'c');\r
1262   if (q)\r
1263     mfp->charset = (BYTE) atoi(q+1);\r
1264 }\r
1265 \r
1266 void\r
1267 ParseFont(char *name, int number)\r
1268 { // wrapper to shield back-end from 'font'\r
1269   ParseFontName(name, &font[boardSize][number]->mfp);\r
1270 }\r
1271 \r
1272 void\r
1273 SetFontDefaults()\r
1274 { // in WB  we have a 2D array of fonts; this initializes their description\r
1275   int i, j;\r
1276   /* Point font array elements to structures and\r
1277      parse default font names */\r
1278   for (i=0; i<NUM_FONTS; i++) {\r
1279     for (j=0; j<NUM_SIZES; j++) {\r
1280       font[j][i] = &fontRec[j][i];\r
1281       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1282     }\r
1283   }\r
1284 }\r
1285 \r
1286 void\r
1287 CreateFonts()\r
1288 { // here we create the actual fonts from the selected descriptions\r
1289   int i, j;\r
1290   for (i=0; i<NUM_FONTS; i++) {\r
1291     for (j=0; j<NUM_SIZES; j++) {\r
1292       CreateFontInMF(font[j][i]);\r
1293     }\r
1294   }\r
1295 }\r
1296 /* Color name parser.\r
1297    X version accepts X color names, but this one\r
1298    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1299 COLORREF\r
1300 ParseColorName(char *name)\r
1301 {\r
1302   int red, green, blue, count;\r
1303   char buf[MSG_SIZ];\r
1304 \r
1305   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1306   if (count != 3) {\r
1307     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1308       &red, &green, &blue);\r
1309   }\r
1310   if (count != 3) {\r
1311     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1312     DisplayError(buf, 0);\r
1313     return RGB(0, 0, 0);\r
1314   }\r
1315   return PALETTERGB(red, green, blue);\r
1316 }\r
1317 \r
1318 void\r
1319 ParseColor(int n, char *name)\r
1320 { // for WinBoard the color is an int, which needs to be derived from the string\r
1321   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1322 }\r
1323 \r
1324 void\r
1325 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1326 {\r
1327   char *e = argValue;\r
1328   int eff = 0;\r
1329 \r
1330   while (*e) {\r
1331     if (*e == 'b')      eff |= CFE_BOLD;\r
1332     else if (*e == 'i') eff |= CFE_ITALIC;\r
1333     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1334     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1335     else if (*e == '#' || isdigit(*e)) break;\r
1336     e++;\r
1337   }\r
1338   *effects = eff;\r
1339   *color   = ParseColorName(e);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseTextAttribs(ColorClass cc, char *s)\r
1344 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1345     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1346     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1347 }\r
1348 \r
1349 void\r
1350 ParseBoardSize(void *addr, char *name)\r
1351 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1352   BoardSize bs = SizeTiny;\r
1353   while (sizeInfo[bs].name != NULL) {\r
1354     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1355         *(BoardSize *)addr = bs;\r
1356         return;\r
1357     }\r
1358     bs++;\r
1359   }\r
1360   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1361 }\r
1362 \r
1363 void\r
1364 LoadAllSounds()\r
1365 { // [HGM] import name from appData first\r
1366   ColorClass cc;\r
1367   SoundClass sc;\r
1368   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1369     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1370     textAttribs[cc].sound.data = NULL;\r
1371     MyLoadSound(&textAttribs[cc].sound);\r
1372   }\r
1373   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1374     textAttribs[cc].sound.name = strdup("");\r
1375     textAttribs[cc].sound.data = NULL;\r
1376   }\r
1377   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1378     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1379     sounds[sc].data = NULL;\r
1380     MyLoadSound(&sounds[sc]);\r
1381   }\r
1382 }\r
1383 \r
1384 void\r
1385 SetCommPortDefaults()\r
1386 {\r
1387    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1388   dcb.DCBlength = sizeof(DCB);\r
1389   dcb.BaudRate = 9600;\r
1390   dcb.fBinary = TRUE;\r
1391   dcb.fParity = FALSE;\r
1392   dcb.fOutxCtsFlow = FALSE;\r
1393   dcb.fOutxDsrFlow = FALSE;\r
1394   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1395   dcb.fDsrSensitivity = FALSE;\r
1396   dcb.fTXContinueOnXoff = TRUE;\r
1397   dcb.fOutX = FALSE;\r
1398   dcb.fInX = FALSE;\r
1399   dcb.fNull = FALSE;\r
1400   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1401   dcb.fAbortOnError = FALSE;\r
1402   dcb.ByteSize = 7;\r
1403   dcb.Parity = SPACEPARITY;\r
1404   dcb.StopBits = ONESTOPBIT;\r
1405 }\r
1406 \r
1407 // [HGM] args: these three cases taken out to stay in front-end\r
1408 void\r
1409 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1410 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1411         // while the curent board size determines the element. This system should be ported to XBoard.\r
1412         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1413         int bs;\r
1414         for (bs=0; bs<NUM_SIZES; bs++) {\r
1415           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1416           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1417           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1418             ad->argName, mfp->faceName, mfp->pointSize,\r
1419             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1420             mfp->bold ? "b" : "",\r
1421             mfp->italic ? "i" : "",\r
1422             mfp->underline ? "u" : "",\r
1423             mfp->strikeout ? "s" : "",\r
1424             (int)mfp->charset);\r
1425         }\r
1426       }\r
1427 \r
1428 void\r
1429 ExportSounds()\r
1430 { // [HGM] copy the names from the internal WB variables to appData\r
1431   ColorClass cc;\r
1432   SoundClass sc;\r
1433   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1434     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1435   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1436     (&appData.soundMove)[sc] = sounds[sc].name;\r
1437 }\r
1438 \r
1439 void\r
1440 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1441 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1442         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1443         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1444           (ta->effects & CFE_BOLD) ? "b" : "",\r
1445           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1446           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1447           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1448           (ta->effects) ? " " : "",\r
1449           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1450       }\r
1451 \r
1452 void\r
1453 SaveColor(FILE *f, ArgDescriptor *ad)\r
1454 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1455         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1456         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1457           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1458 }\r
1459 \r
1460 void\r
1461 SaveBoardSize(FILE *f, char *name, void *addr)\r
1462 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1463   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1464 }\r
1465 \r
1466 void\r
1467 ParseCommPortSettings(char *s)\r
1468 { // wrapper to keep dcb from back-end\r
1469   ParseCommSettings(s, &dcb);\r
1470 }\r
1471 \r
1472 void\r
1473 GetWindowCoords()\r
1474 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1475   GetActualPlacement(hwndMain, &wpMain);\r
1476   GetActualPlacement(hwndConsole, &wpConsole);\r
1477   GetActualPlacement(commentDialog, &wpComment);\r
1478   GetActualPlacement(editTagsDialog, &wpTags);\r
1479   GetActualPlacement(gameListDialog, &wpGameList);\r
1480   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1481   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1482   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1483 }\r
1484 \r
1485 void\r
1486 PrintCommPortSettings(FILE *f, char *name)\r
1487 { // wrapper to shield back-end from DCB\r
1488       PrintCommSettings(f, name, &dcb);\r
1489 }\r
1490 \r
1491 int\r
1492 MySearchPath(char *installDir, char *name, char *fullname)\r
1493 {\r
1494   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1495   if(name[0]== '%') {\r
1496     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1497     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1498       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1499       *strchr(buf, '%') = 0;\r
1500       strcat(fullname, getenv(buf));\r
1501       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1502     }\r
1503     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1504     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1505     return (int) strlen(fullname);\r
1506   }\r
1507   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1508 }\r
1509 \r
1510 int\r
1511 MyGetFullPathName(char *name, char *fullname)\r
1512 {\r
1513   char *dummy;\r
1514   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1515 }\r
1516 \r
1517 int\r
1518 MainWindowUp()\r
1519 { // [HGM] args: allows testing if main window is realized from back-end\r
1520   return hwndMain != NULL;\r
1521 }\r
1522 \r
1523 void\r
1524 PopUpStartupDialog()\r
1525 {\r
1526     FARPROC lpProc;\r
1527     \r
1528     LoadLanguageFile(appData.language);\r
1529     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1530     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1531     FreeProcInstance(lpProc);\r
1532 }\r
1533 \r
1534 /*---------------------------------------------------------------------------*\\r
1535  *\r
1536  * GDI board drawing routines\r
1537  *\r
1538 \*---------------------------------------------------------------------------*/\r
1539 \r
1540 /* [AS] Draw square using background texture */\r
1541 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1542 {\r
1543     XFORM   x;\r
1544 \r
1545     if( mode == 0 ) {\r
1546         return; /* Should never happen! */\r
1547     }\r
1548 \r
1549     SetGraphicsMode( dst, GM_ADVANCED );\r
1550 \r
1551     switch( mode ) {\r
1552     case 1:\r
1553         /* Identity */\r
1554         break;\r
1555     case 2:\r
1556         /* X reflection */\r
1557         x.eM11 = -1.0;\r
1558         x.eM12 = 0;\r
1559         x.eM21 = 0;\r
1560         x.eM22 = 1.0;\r
1561         x.eDx = (FLOAT) dw + dx - 1;\r
1562         x.eDy = 0;\r
1563         dx = 0;\r
1564         SetWorldTransform( dst, &x );\r
1565         break;\r
1566     case 3:\r
1567         /* Y reflection */\r
1568         x.eM11 = 1.0;\r
1569         x.eM12 = 0;\r
1570         x.eM21 = 0;\r
1571         x.eM22 = -1.0;\r
1572         x.eDx = 0;\r
1573         x.eDy = (FLOAT) dh + dy - 1;\r
1574         dy = 0;\r
1575         SetWorldTransform( dst, &x );\r
1576         break;\r
1577     case 4:\r
1578         /* X/Y flip */\r
1579         x.eM11 = 0;\r
1580         x.eM12 = 1.0;\r
1581         x.eM21 = 1.0;\r
1582         x.eM22 = 0;\r
1583         x.eDx = (FLOAT) dx;\r
1584         x.eDy = (FLOAT) dy;\r
1585         dx = 0;\r
1586         dy = 0;\r
1587         SetWorldTransform( dst, &x );\r
1588         break;\r
1589     }\r
1590 \r
1591     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1592 \r
1593     x.eM11 = 1.0;\r
1594     x.eM12 = 0;\r
1595     x.eM21 = 0;\r
1596     x.eM22 = 1.0;\r
1597     x.eDx = 0;\r
1598     x.eDy = 0;\r
1599     SetWorldTransform( dst, &x );\r
1600 \r
1601     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1602 }\r
1603 \r
1604 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1605 enum {\r
1606     PM_WP = (int) WhitePawn, \r
1607     PM_WN = (int) WhiteKnight, \r
1608     PM_WB = (int) WhiteBishop, \r
1609     PM_WR = (int) WhiteRook, \r
1610     PM_WQ = (int) WhiteQueen, \r
1611     PM_WF = (int) WhiteFerz, \r
1612     PM_WW = (int) WhiteWazir, \r
1613     PM_WE = (int) WhiteAlfil, \r
1614     PM_WM = (int) WhiteMan, \r
1615     PM_WO = (int) WhiteCannon, \r
1616     PM_WU = (int) WhiteUnicorn, \r
1617     PM_WH = (int) WhiteNightrider, \r
1618     PM_WA = (int) WhiteAngel, \r
1619     PM_WC = (int) WhiteMarshall, \r
1620     PM_WAB = (int) WhiteCardinal, \r
1621     PM_WD = (int) WhiteDragon, \r
1622     PM_WL = (int) WhiteLance, \r
1623     PM_WS = (int) WhiteCobra, \r
1624     PM_WV = (int) WhiteFalcon, \r
1625     PM_WSG = (int) WhiteSilver, \r
1626     PM_WG = (int) WhiteGrasshopper, \r
1627     PM_WK = (int) WhiteKing,\r
1628     PM_BP = (int) BlackPawn, \r
1629     PM_BN = (int) BlackKnight, \r
1630     PM_BB = (int) BlackBishop, \r
1631     PM_BR = (int) BlackRook, \r
1632     PM_BQ = (int) BlackQueen, \r
1633     PM_BF = (int) BlackFerz, \r
1634     PM_BW = (int) BlackWazir, \r
1635     PM_BE = (int) BlackAlfil, \r
1636     PM_BM = (int) BlackMan,\r
1637     PM_BO = (int) BlackCannon, \r
1638     PM_BU = (int) BlackUnicorn, \r
1639     PM_BH = (int) BlackNightrider, \r
1640     PM_BA = (int) BlackAngel, \r
1641     PM_BC = (int) BlackMarshall, \r
1642     PM_BG = (int) BlackGrasshopper, \r
1643     PM_BAB = (int) BlackCardinal,\r
1644     PM_BD = (int) BlackDragon,\r
1645     PM_BL = (int) BlackLance,\r
1646     PM_BS = (int) BlackCobra,\r
1647     PM_BV = (int) BlackFalcon,\r
1648     PM_BSG = (int) BlackSilver,\r
1649     PM_BK = (int) BlackKing\r
1650 };\r
1651 \r
1652 static HFONT hPieceFont = NULL;\r
1653 static HBITMAP hPieceMask[(int) EmptySquare];\r
1654 static HBITMAP hPieceFace[(int) EmptySquare];\r
1655 static int fontBitmapSquareSize = 0;\r
1656 static char pieceToFontChar[(int) EmptySquare] =\r
1657                               { 'p', 'n', 'b', 'r', 'q', \r
1658                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1659                       'k', 'o', 'm', 'v', 't', 'w', \r
1660                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1661                                                               'l' };\r
1662 \r
1663 extern BOOL SetCharTable( char *table, const char * map );\r
1664 /* [HGM] moved to backend.c */\r
1665 \r
1666 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1667 {\r
1668     HBRUSH hbrush;\r
1669     BYTE r1 = GetRValue( color );\r
1670     BYTE g1 = GetGValue( color );\r
1671     BYTE b1 = GetBValue( color );\r
1672     BYTE r2 = r1 / 2;\r
1673     BYTE g2 = g1 / 2;\r
1674     BYTE b2 = b1 / 2;\r
1675     RECT rc;\r
1676 \r
1677     /* Create a uniform background first */\r
1678     hbrush = CreateSolidBrush( color );\r
1679     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1680     FillRect( hdc, &rc, hbrush );\r
1681     DeleteObject( hbrush );\r
1682     \r
1683     if( mode == 1 ) {\r
1684         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1685         int steps = squareSize / 2;\r
1686         int i;\r
1687 \r
1688         for( i=0; i<steps; i++ ) {\r
1689             BYTE r = r1 - (r1-r2) * i / steps;\r
1690             BYTE g = g1 - (g1-g2) * i / steps;\r
1691             BYTE b = b1 - (b1-b2) * i / steps;\r
1692 \r
1693             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1694             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1695             FillRect( hdc, &rc, hbrush );\r
1696             DeleteObject(hbrush);\r
1697         }\r
1698     }\r
1699     else if( mode == 2 ) {\r
1700         /* Diagonal gradient, good more or less for every piece */\r
1701         POINT triangle[3];\r
1702         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1703         HBRUSH hbrush_old;\r
1704         int steps = squareSize;\r
1705         int i;\r
1706 \r
1707         triangle[0].x = squareSize - steps;\r
1708         triangle[0].y = squareSize;\r
1709         triangle[1].x = squareSize;\r
1710         triangle[1].y = squareSize;\r
1711         triangle[2].x = squareSize;\r
1712         triangle[2].y = squareSize - steps;\r
1713 \r
1714         for( i=0; i<steps; i++ ) {\r
1715             BYTE r = r1 - (r1-r2) * i / steps;\r
1716             BYTE g = g1 - (g1-g2) * i / steps;\r
1717             BYTE b = b1 - (b1-b2) * i / steps;\r
1718 \r
1719             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1720             hbrush_old = SelectObject( hdc, hbrush );\r
1721             Polygon( hdc, triangle, 3 );\r
1722             SelectObject( hdc, hbrush_old );\r
1723             DeleteObject(hbrush);\r
1724             triangle[0].x++;\r
1725             triangle[2].y++;\r
1726         }\r
1727 \r
1728         SelectObject( hdc, hpen );\r
1729     }\r
1730 }\r
1731 \r
1732 /*\r
1733     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1734     seems to work ok. The main problem here is to find the "inside" of a chess\r
1735     piece: follow the steps as explained below.\r
1736 */\r
1737 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1738 {\r
1739     HBITMAP hbm;\r
1740     HBITMAP hbm_old;\r
1741     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1742     RECT rc;\r
1743     SIZE sz;\r
1744     POINT pt;\r
1745     int backColor = whitePieceColor; \r
1746     int foreColor = blackPieceColor;\r
1747     \r
1748     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1749         backColor = appData.fontBackColorWhite;\r
1750         foreColor = appData.fontForeColorWhite;\r
1751     }\r
1752     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1753         backColor = appData.fontBackColorBlack;\r
1754         foreColor = appData.fontForeColorBlack;\r
1755     }\r
1756 \r
1757     /* Mask */\r
1758     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1759 \r
1760     hbm_old = SelectObject( hdc, hbm );\r
1761 \r
1762     rc.left = 0;\r
1763     rc.top = 0;\r
1764     rc.right = squareSize;\r
1765     rc.bottom = squareSize;\r
1766 \r
1767     /* Step 1: background is now black */\r
1768     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1769 \r
1770     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1771 \r
1772     pt.x = (squareSize - sz.cx) / 2;\r
1773     pt.y = (squareSize - sz.cy) / 2;\r
1774 \r
1775     SetBkMode( hdc, TRANSPARENT );\r
1776     SetTextColor( hdc, chroma );\r
1777     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1778     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1779 \r
1780     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1781     /* Step 3: the area outside the piece is filled with white */\r
1782 //    FloodFill( hdc, 0, 0, chroma );\r
1783     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1784     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1785     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1786     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1787     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1788     /* \r
1789         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1790         but if the start point is not inside the piece we're lost!\r
1791         There should be a better way to do this... if we could create a region or path\r
1792         from the fill operation we would be fine for example.\r
1793     */\r
1794 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1795     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1796 \r
1797     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1798         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1799         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1800 \r
1801         SelectObject( dc2, bm2 );\r
1802         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1803         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1804         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1805         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1806         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807 \r
1808         DeleteDC( dc2 );\r
1809         DeleteObject( bm2 );\r
1810     }\r
1811 \r
1812     SetTextColor( hdc, 0 );\r
1813     /* \r
1814         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1815         draw the piece again in black for safety.\r
1816     */\r
1817     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1818 \r
1819     SelectObject( hdc, hbm_old );\r
1820 \r
1821     if( hPieceMask[index] != NULL ) {\r
1822         DeleteObject( hPieceMask[index] );\r
1823     }\r
1824 \r
1825     hPieceMask[index] = hbm;\r
1826 \r
1827     /* Face */\r
1828     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830     SelectObject( hdc, hbm );\r
1831 \r
1832     {\r
1833         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1834         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1835         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1836 \r
1837         SelectObject( dc1, hPieceMask[index] );\r
1838         SelectObject( dc2, bm2 );\r
1839         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1840         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1841         \r
1842         /* \r
1843             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1844             the piece background and deletes (makes transparent) the rest.\r
1845             Thanks to that mask, we are free to paint the background with the greates\r
1846             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1847             We use this, to make gradients and give the pieces a "roundish" look.\r
1848         */\r
1849         SetPieceBackground( hdc, backColor, 2 );\r
1850         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1851 \r
1852         DeleteDC( dc2 );\r
1853         DeleteDC( dc1 );\r
1854         DeleteObject( bm2 );\r
1855     }\r
1856 \r
1857     SetTextColor( hdc, foreColor );\r
1858     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1859 \r
1860     SelectObject( hdc, hbm_old );\r
1861 \r
1862     if( hPieceFace[index] != NULL ) {\r
1863         DeleteObject( hPieceFace[index] );\r
1864     }\r
1865 \r
1866     hPieceFace[index] = hbm;\r
1867 }\r
1868 \r
1869 static int TranslatePieceToFontPiece( int piece )\r
1870 {\r
1871     switch( piece ) {\r
1872     case BlackPawn:\r
1873         return PM_BP;\r
1874     case BlackKnight:\r
1875         return PM_BN;\r
1876     case BlackBishop:\r
1877         return PM_BB;\r
1878     case BlackRook:\r
1879         return PM_BR;\r
1880     case BlackQueen:\r
1881         return PM_BQ;\r
1882     case BlackKing:\r
1883         return PM_BK;\r
1884     case WhitePawn:\r
1885         return PM_WP;\r
1886     case WhiteKnight:\r
1887         return PM_WN;\r
1888     case WhiteBishop:\r
1889         return PM_WB;\r
1890     case WhiteRook:\r
1891         return PM_WR;\r
1892     case WhiteQueen:\r
1893         return PM_WQ;\r
1894     case WhiteKing:\r
1895         return PM_WK;\r
1896 \r
1897     case BlackAngel:\r
1898         return PM_BA;\r
1899     case BlackMarshall:\r
1900         return PM_BC;\r
1901     case BlackFerz:\r
1902         return PM_BF;\r
1903     case BlackNightrider:\r
1904         return PM_BH;\r
1905     case BlackAlfil:\r
1906         return PM_BE;\r
1907     case BlackWazir:\r
1908         return PM_BW;\r
1909     case BlackUnicorn:\r
1910         return PM_BU;\r
1911     case BlackCannon:\r
1912         return PM_BO;\r
1913     case BlackGrasshopper:\r
1914         return PM_BG;\r
1915     case BlackMan:\r
1916         return PM_BM;\r
1917     case BlackSilver:\r
1918         return PM_BSG;\r
1919     case BlackLance:\r
1920         return PM_BL;\r
1921     case BlackFalcon:\r
1922         return PM_BV;\r
1923     case BlackCobra:\r
1924         return PM_BS;\r
1925     case BlackCardinal:\r
1926         return PM_BAB;\r
1927     case BlackDragon:\r
1928         return PM_BD;\r
1929 \r
1930     case WhiteAngel:\r
1931         return PM_WA;\r
1932     case WhiteMarshall:\r
1933         return PM_WC;\r
1934     case WhiteFerz:\r
1935         return PM_WF;\r
1936     case WhiteNightrider:\r
1937         return PM_WH;\r
1938     case WhiteAlfil:\r
1939         return PM_WE;\r
1940     case WhiteWazir:\r
1941         return PM_WW;\r
1942     case WhiteUnicorn:\r
1943         return PM_WU;\r
1944     case WhiteCannon:\r
1945         return PM_WO;\r
1946     case WhiteGrasshopper:\r
1947         return PM_WG;\r
1948     case WhiteMan:\r
1949         return PM_WM;\r
1950     case WhiteSilver:\r
1951         return PM_WSG;\r
1952     case WhiteLance:\r
1953         return PM_WL;\r
1954     case WhiteFalcon:\r
1955         return PM_WV;\r
1956     case WhiteCobra:\r
1957         return PM_WS;\r
1958     case WhiteCardinal:\r
1959         return PM_WAB;\r
1960     case WhiteDragon:\r
1961         return PM_WD;\r
1962     }\r
1963 \r
1964     return 0;\r
1965 }\r
1966 \r
1967 void CreatePiecesFromFont()\r
1968 {\r
1969     LOGFONT lf;\r
1970     HDC hdc_window = NULL;\r
1971     HDC hdc = NULL;\r
1972     HFONT hfont_old;\r
1973     int fontHeight;\r
1974     int i;\r
1975 \r
1976     if( fontBitmapSquareSize < 0 ) {\r
1977         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1978         return;\r
1979     }\r
1980 \r
1981     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
1982             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1983         fontBitmapSquareSize = -1;\r
1984         return;\r
1985     }\r
1986 \r
1987     if( fontBitmapSquareSize != squareSize ) {\r
1988         hdc_window = GetDC( hwndMain );\r
1989         hdc = CreateCompatibleDC( hdc_window );\r
1990 \r
1991         if( hPieceFont != NULL ) {\r
1992             DeleteObject( hPieceFont );\r
1993         }\r
1994         else {\r
1995             for( i=0; i<=(int)BlackKing; i++ ) {\r
1996                 hPieceMask[i] = NULL;\r
1997                 hPieceFace[i] = NULL;\r
1998             }\r
1999         }\r
2000 \r
2001         fontHeight = 75;\r
2002 \r
2003         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2004             fontHeight = appData.fontPieceSize;\r
2005         }\r
2006 \r
2007         fontHeight = (fontHeight * squareSize) / 100;\r
2008 \r
2009         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2010         lf.lfWidth = 0;\r
2011         lf.lfEscapement = 0;\r
2012         lf.lfOrientation = 0;\r
2013         lf.lfWeight = FW_NORMAL;\r
2014         lf.lfItalic = 0;\r
2015         lf.lfUnderline = 0;\r
2016         lf.lfStrikeOut = 0;\r
2017         lf.lfCharSet = DEFAULT_CHARSET;\r
2018         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2019         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2020         lf.lfQuality = PROOF_QUALITY;\r
2021         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2022         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2023         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2024 \r
2025         hPieceFont = CreateFontIndirect( &lf );\r
2026 \r
2027         if( hPieceFont == NULL ) {\r
2028             fontBitmapSquareSize = -2;\r
2029         }\r
2030         else {\r
2031             /* Setup font-to-piece character table */\r
2032             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2033                 /* No (or wrong) global settings, try to detect the font */\r
2034                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2035                     /* Alpha */\r
2036                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2039                     /* DiagramTT* family */\r
2040                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2041                 }\r
2042                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2043                     /* Fairy symbols */\r
2044                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2045                 }\r
2046                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2047                     /* Good Companion (Some characters get warped as literal :-( */\r
2048                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2049                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2050                     SetCharTable(pieceToFontChar, s);\r
2051                 }\r
2052                 else {\r
2053                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2054                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2055                 }\r
2056             }\r
2057 \r
2058             /* Create bitmaps */\r
2059             hfont_old = SelectObject( hdc, hPieceFont );\r
2060             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2061                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2062                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2063 \r
2064             SelectObject( hdc, hfont_old );\r
2065 \r
2066             fontBitmapSquareSize = squareSize;\r
2067         }\r
2068     }\r
2069 \r
2070     if( hdc != NULL ) {\r
2071         DeleteDC( hdc );\r
2072     }\r
2073 \r
2074     if( hdc_window != NULL ) {\r
2075         ReleaseDC( hwndMain, hdc_window );\r
2076     }\r
2077 }\r
2078 \r
2079 HBITMAP\r
2080 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2081 {\r
2082   char name[128];\r
2083 \r
2084     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2085   if (gameInfo.event &&\r
2086       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2087       strcmp(name, "k80s") == 0) {\r
2088     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2089   }\r
2090   return LoadBitmap(hinst, name);\r
2091 }\r
2092 \r
2093 \r
2094 /* Insert a color into the program's logical palette\r
2095    structure.  This code assumes the given color is\r
2096    the result of the RGB or PALETTERGB macro, and it\r
2097    knows how those macros work (which is documented).\r
2098 */\r
2099 VOID\r
2100 InsertInPalette(COLORREF color)\r
2101 {\r
2102   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2103 \r
2104   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2105     DisplayFatalError(_("Too many colors"), 0, 1);\r
2106     pLogPal->palNumEntries--;\r
2107     return;\r
2108   }\r
2109 \r
2110   pe->peFlags = (char) 0;\r
2111   pe->peRed = (char) (0xFF & color);\r
2112   pe->peGreen = (char) (0xFF & (color >> 8));\r
2113   pe->peBlue = (char) (0xFF & (color >> 16));\r
2114   return;\r
2115 }\r
2116 \r
2117 \r
2118 VOID\r
2119 InitDrawingColors()\r
2120 {\r
2121   if (pLogPal == NULL) {\r
2122     /* Allocate enough memory for a logical palette with\r
2123      * PALETTESIZE entries and set the size and version fields\r
2124      * of the logical palette structure.\r
2125      */\r
2126     pLogPal = (NPLOGPALETTE)\r
2127       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2128                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2129     pLogPal->palVersion    = 0x300;\r
2130   }\r
2131   pLogPal->palNumEntries = 0;\r
2132 \r
2133   InsertInPalette(lightSquareColor);\r
2134   InsertInPalette(darkSquareColor);\r
2135   InsertInPalette(whitePieceColor);\r
2136   InsertInPalette(blackPieceColor);\r
2137   InsertInPalette(highlightSquareColor);\r
2138   InsertInPalette(premoveHighlightColor);\r
2139 \r
2140   /*  create a logical color palette according the information\r
2141    *  in the LOGPALETTE structure.\r
2142    */\r
2143   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2144 \r
2145   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2146   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2147   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2148   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2149   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2150   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2151   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2152   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2153   /* [AS] Force rendering of the font-based pieces */\r
2154   if( fontBitmapSquareSize > 0 ) {\r
2155     fontBitmapSquareSize = 0;\r
2156   }\r
2157 }\r
2158 \r
2159 \r
2160 int\r
2161 BoardWidth(int boardSize, int n)\r
2162 { /* [HGM] argument n added to allow different width and height */\r
2163   int lineGap = sizeInfo[boardSize].lineGap;\r
2164 \r
2165   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2166       lineGap = appData.overrideLineGap;\r
2167   }\r
2168 \r
2169   return (n + 1) * lineGap +\r
2170           n * sizeInfo[boardSize].squareSize;\r
2171 }\r
2172 \r
2173 /* Respond to board resize by dragging edge */\r
2174 VOID\r
2175 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2176 {\r
2177   BoardSize newSize = NUM_SIZES - 1;\r
2178   static int recurse = 0;\r
2179   if (IsIconic(hwndMain)) return;\r
2180   if (recurse > 0) return;\r
2181   recurse++;\r
2182   while (newSize > 0) {\r
2183         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2184         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2185            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2186     newSize--;\r
2187   } \r
2188   boardSize = newSize;\r
2189   InitDrawingSizes(boardSize, flags);\r
2190   recurse--;\r
2191 }\r
2192 \r
2193 \r
2194 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2195 \r
2196 VOID\r
2197 InitDrawingSizes(BoardSize boardSize, int flags)\r
2198 {\r
2199   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2200   ChessSquare piece;\r
2201   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2202   HDC hdc;\r
2203   SIZE clockSize, messageSize;\r
2204   HFONT oldFont;\r
2205   char buf[MSG_SIZ];\r
2206   char *str;\r
2207   HMENU hmenu = GetMenu(hwndMain);\r
2208   RECT crect, wrect, oldRect;\r
2209   int offby;\r
2210   LOGBRUSH logbrush;\r
2211 \r
2212   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2213   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2214 \r
2215   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2216   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2217 \r
2218   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2219   oldRect.top = wpMain.y;\r
2220   oldRect.right = wpMain.x + wpMain.width;\r
2221   oldRect.bottom = wpMain.y + wpMain.height;\r
2222 \r
2223   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2224   smallLayout = sizeInfo[boardSize].smallLayout;\r
2225   squareSize = sizeInfo[boardSize].squareSize;\r
2226   lineGap = sizeInfo[boardSize].lineGap;\r
2227   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2228 \r
2229   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2230       lineGap = appData.overrideLineGap;\r
2231   }\r
2232 \r
2233   if (tinyLayout != oldTinyLayout) {\r
2234     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2235     if (tinyLayout) {\r
2236       style &= ~WS_SYSMENU;\r
2237       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2238                  "&Minimize\tCtrl+F4");\r
2239     } else {\r
2240       style |= WS_SYSMENU;\r
2241       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2242     }\r
2243     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2244 \r
2245     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2246       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2247         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2248     }\r
2249     DrawMenuBar(hwndMain);\r
2250   }\r
2251 \r
2252   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2253   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2254 \r
2255   /* Get text area sizes */\r
2256   hdc = GetDC(hwndMain);\r
2257   if (appData.clockMode) {\r
2258     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2259   } else {\r
2260     snprintf(buf, MSG_SIZ, _("White"));\r
2261   }\r
2262   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2263   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2264   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2265   str = _("We only care about the height here");\r
2266   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2267   SelectObject(hdc, oldFont);\r
2268   ReleaseDC(hwndMain, hdc);\r
2269 \r
2270   /* Compute where everything goes */\r
2271   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2272         /* [HGM] logo: if either logo is on, reserve space for it */\r
2273         logoHeight =  2*clockSize.cy;\r
2274         leftLogoRect.left   = OUTER_MARGIN;\r
2275         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2276         leftLogoRect.top    = OUTER_MARGIN;\r
2277         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2278 \r
2279         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2280         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2281         rightLogoRect.top    = OUTER_MARGIN;\r
2282         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2283 \r
2284 \r
2285     whiteRect.left = leftLogoRect.right;\r
2286     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2287     whiteRect.top = OUTER_MARGIN;\r
2288     whiteRect.bottom = whiteRect.top + logoHeight;\r
2289 \r
2290     blackRect.right = rightLogoRect.left;\r
2291     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2292     blackRect.top = whiteRect.top;\r
2293     blackRect.bottom = whiteRect.bottom;\r
2294   } else {\r
2295     whiteRect.left = OUTER_MARGIN;\r
2296     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2297     whiteRect.top = OUTER_MARGIN;\r
2298     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2299 \r
2300     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2301     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2302     blackRect.top = whiteRect.top;\r
2303     blackRect.bottom = whiteRect.bottom;\r
2304 \r
2305     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2306   }\r
2307 \r
2308   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2309   if (appData.showButtonBar) {\r
2310     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2311       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2312   } else {\r
2313     messageRect.right = OUTER_MARGIN + boardWidth;\r
2314   }\r
2315   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2316   messageRect.bottom = messageRect.top + messageSize.cy;\r
2317 \r
2318   boardRect.left = OUTER_MARGIN;\r
2319   boardRect.right = boardRect.left + boardWidth;\r
2320   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2321   boardRect.bottom = boardRect.top + boardHeight;\r
2322 \r
2323   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2324   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2325   oldBoardSize = boardSize;\r
2326   oldTinyLayout = tinyLayout;\r
2327   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2328   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2329     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2330   winW *= 1 + twoBoards;\r
2331   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2332   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2333   wpMain.height = winH; //       without disturbing window attachments\r
2334   GetWindowRect(hwndMain, &wrect);\r
2335   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2336                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2337 \r
2338   // [HGM] placement: let attached windows follow size change.\r
2339   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2340   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2341   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2344 \r
2345   /* compensate if menu bar wrapped */\r
2346   GetClientRect(hwndMain, &crect);\r
2347   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2348   wpMain.height += offby;\r
2349   switch (flags) {\r
2350   case WMSZ_TOPLEFT:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_TOPRIGHT:\r
2357   case WMSZ_TOP:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.left, wrect.bottom - wpMain.height, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMLEFT:\r
2364   case WMSZ_LEFT:\r
2365     SetWindowPos(hwndMain, NULL, \r
2366                  wrect.right - wpMain.width, wrect.top, \r
2367                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2368     break;\r
2369 \r
2370   case WMSZ_BOTTOMRIGHT:\r
2371   case WMSZ_BOTTOM:\r
2372   case WMSZ_RIGHT:\r
2373   default:\r
2374     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2375                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2376     break;\r
2377   }\r
2378 \r
2379   hwndPause = NULL;\r
2380   for (i = 0; i < N_BUTTONS; i++) {\r
2381     if (buttonDesc[i].hwnd != NULL) {\r
2382       DestroyWindow(buttonDesc[i].hwnd);\r
2383       buttonDesc[i].hwnd = NULL;\r
2384     }\r
2385     if (appData.showButtonBar) {\r
2386       buttonDesc[i].hwnd =\r
2387         CreateWindow("BUTTON", buttonDesc[i].label,\r
2388                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2389                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2390                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2391                      (HMENU) buttonDesc[i].id,\r
2392                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2393       if (tinyLayout) {\r
2394         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2395                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2396                     MAKELPARAM(FALSE, 0));\r
2397       }\r
2398       if (buttonDesc[i].id == IDM_Pause)\r
2399         hwndPause = buttonDesc[i].hwnd;\r
2400       buttonDesc[i].wndproc = (WNDPROC)\r
2401         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2402     }\r
2403   }\r
2404   if (gridPen != NULL) DeleteObject(gridPen);\r
2405   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2406   if (premovePen != NULL) DeleteObject(premovePen);\r
2407   if (lineGap != 0) {\r
2408     logbrush.lbStyle = BS_SOLID;\r
2409     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2410     gridPen =\r
2411       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2412                    lineGap, &logbrush, 0, NULL);\r
2413     logbrush.lbColor = highlightSquareColor;\r
2414     highlightPen =\r
2415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2416                    lineGap, &logbrush, 0, NULL);\r
2417 \r
2418     logbrush.lbColor = premoveHighlightColor; \r
2419     premovePen =\r
2420       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2421                    lineGap, &logbrush, 0, NULL);\r
2422 \r
2423     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2424     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2425       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2426       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2427         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2428       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2429         BOARD_WIDTH * (squareSize + lineGap);\r
2430       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2431     }\r
2432     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2433       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2434       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2435         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2436         lineGap / 2 + (i * (squareSize + lineGap));\r
2437       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2438         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2439       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2440     }\r
2441   }\r
2442 \r
2443   /* [HGM] Licensing requirement */\r
2444 #ifdef GOTHIC\r
2445   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2446 #endif\r
2447 #ifdef FALCON\r
2448   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2449 #endif\r
2450   GothicPopUp( "", VariantNormal);\r
2451 \r
2452 \r
2453 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2454 \r
2455   /* Load piece bitmaps for this board size */\r
2456   for (i=0; i<=2; i++) {\r
2457     for (piece = WhitePawn;\r
2458          (int) piece < (int) BlackPawn;\r
2459          piece = (ChessSquare) ((int) piece + 1)) {\r
2460       if (pieceBitmap[i][piece] != NULL)\r
2461         DeleteObject(pieceBitmap[i][piece]);\r
2462     }\r
2463   }\r
2464 \r
2465   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2466   // Orthodox Chess pieces\r
2467   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2468   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2469   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2470   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2471   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2472   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2473   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2474   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2475   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2476   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2477   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2478   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2479   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2480   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2481   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2482   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2483     // in Shogi, Hijack the unused Queen for Lance\r
2484     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2485     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2486     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2487   } else {\r
2488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2491   }\r
2492 \r
2493   if(squareSize <= 72 && squareSize >= 33) { \r
2494     /* A & C are available in most sizes now */\r
2495     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2496       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2497       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2498       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2499       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2500       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2501       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2502       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2503       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2504       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2505       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2506       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2507       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2508     } else { // Smirf-like\r
2509       if(gameInfo.variant == VariantSChess) {\r
2510         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2511         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2512         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2513       } else {\r
2514         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2515         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2516         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2517       }\r
2518     }\r
2519     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2523     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2524       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2525       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2526       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2527     } else { // WinBoard standard\r
2528       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2529       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2530       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2531     }\r
2532   }\r
2533 \r
2534 \r
2535   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2536     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2537     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2538     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2539     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2540     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2541     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2542     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2543     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2544     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2545     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2546     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2547     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2548     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2549     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2550     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2551     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2552     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2553     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2554     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2555     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2556     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2557     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2558     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2559     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2560     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2561     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2562     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2563     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2564     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2565     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2566 \r
2567     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2568       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2569       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2570       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2571       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2572       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2573       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2574       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2575       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2576       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2577       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2578       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2579       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2580     } else {\r
2581       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2582       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2583       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2584       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2585       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2586       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2587       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2588       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2589       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2590       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2591       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2592       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2593     }\r
2594 \r
2595   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2596     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2597     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2598     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2599     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2600     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2601     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2602     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2603     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2604     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2605     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2606     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2607     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2608     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2609     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2610   }\r
2611 \r
2612 \r
2613   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2614   /* special Shogi support in this size */\r
2615   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2616       for (piece = WhitePawn;\r
2617            (int) piece < (int) BlackPawn;\r
2618            piece = (ChessSquare) ((int) piece + 1)) {\r
2619         if (pieceBitmap[i][piece] != NULL)\r
2620           DeleteObject(pieceBitmap[i][piece]);\r
2621       }\r
2622     }\r
2623   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2624   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2625   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2626   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2627   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2628   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2629   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2630   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2631   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2632   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2633   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2634   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2635   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2636   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2637   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2638   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2639   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2640   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2641   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2642   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2643   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2644   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2645   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2646   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2647   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2648   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2649   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2650   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2651   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2657   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2659   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2660   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2661   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2662   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2663   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2664   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2665   minorSize = 0;\r
2666   }\r
2667 }\r
2668 \r
2669 HBITMAP\r
2670 PieceBitmap(ChessSquare p, int kind)\r
2671 {\r
2672   if ((int) p >= (int) BlackPawn)\r
2673     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2674 \r
2675   return pieceBitmap[kind][(int) p];\r
2676 }\r
2677 \r
2678 /***************************************************************/\r
2679 \r
2680 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2681 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2682 /*\r
2683 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2684 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2685 */\r
2686 \r
2687 VOID\r
2688 SquareToPos(int row, int column, int * x, int * y)\r
2689 {\r
2690   if (flipView) {\r
2691     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2692     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2693   } else {\r
2694     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2695     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2696   }\r
2697 }\r
2698 \r
2699 VOID\r
2700 DrawCoordsOnDC(HDC hdc)\r
2701 {\r
2702   static char files[] = "0123456789012345678901221098765432109876543210";\r
2703   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2704   char str[2] = { NULLCHAR, NULLCHAR };\r
2705   int oldMode, oldAlign, x, y, start, i;\r
2706   HFONT oldFont;\r
2707   HBRUSH oldBrush;\r
2708 \r
2709   if (!appData.showCoords)\r
2710     return;\r
2711 \r
2712   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2713 \r
2714   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2715   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2716   oldAlign = GetTextAlign(hdc);\r
2717   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2718 \r
2719   y = boardRect.top + lineGap;\r
2720   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2721 \r
2722   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2723   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2724     str[0] = files[start + i];\r
2725     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2726     y += squareSize + lineGap;\r
2727   }\r
2728 \r
2729   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2730 \r
2731   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2732   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2733     str[0] = ranks[start + i];\r
2734     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2735     x += squareSize + lineGap;\r
2736   }    \r
2737 \r
2738   SelectObject(hdc, oldBrush);\r
2739   SetBkMode(hdc, oldMode);\r
2740   SetTextAlign(hdc, oldAlign);\r
2741   SelectObject(hdc, oldFont);\r
2742 }\r
2743 \r
2744 VOID\r
2745 DrawGridOnDC(HDC hdc)\r
2746 {\r
2747   HPEN oldPen;\r
2748  \r
2749   if (lineGap != 0) {\r
2750     oldPen = SelectObject(hdc, gridPen);\r
2751     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2752     SelectObject(hdc, oldPen);\r
2753   }\r
2754 }\r
2755 \r
2756 #define HIGHLIGHT_PEN 0\r
2757 #define PREMOVE_PEN   1\r
2758 \r
2759 VOID\r
2760 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2761 {\r
2762   int x1, y1;\r
2763   HPEN oldPen, hPen;\r
2764   if (lineGap == 0) return;\r
2765   if (flipView) {\r
2766     x1 = boardRect.left +\r
2767       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2768     y1 = boardRect.top +\r
2769       lineGap/2 + y * (squareSize + lineGap);\r
2770   } else {\r
2771     x1 = boardRect.left +\r
2772       lineGap/2 + x * (squareSize + lineGap);\r
2773     y1 = boardRect.top +\r
2774       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2775   }\r
2776   hPen = pen ? premovePen : highlightPen;\r
2777   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2778   MoveToEx(hdc, x1, y1, NULL);\r
2779   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2780   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2781   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2782   LineTo(hdc, x1, y1);\r
2783   SelectObject(hdc, oldPen);\r
2784 }\r
2785 \r
2786 VOID\r
2787 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2788 {\r
2789   int i;\r
2790   for (i=0; i<2; i++) {\r
2791     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2792       DrawHighlightOnDC(hdc, TRUE,\r
2793                         h->sq[i].x, h->sq[i].y,\r
2794                         pen);\r
2795   }\r
2796 }\r
2797 \r
2798 /* Note: sqcolor is used only in monoMode */\r
2799 /* Note that this code is largely duplicated in woptions.c,\r
2800    function DrawSampleSquare, so that needs to be updated too */\r
2801 VOID\r
2802 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2803 {\r
2804   HBITMAP oldBitmap;\r
2805   HBRUSH oldBrush;\r
2806   int tmpSize;\r
2807 \r
2808   if (appData.blindfold) return;\r
2809 \r
2810   /* [AS] Use font-based pieces if needed */\r
2811   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2812     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2813     CreatePiecesFromFont();\r
2814 \r
2815     if( fontBitmapSquareSize == squareSize ) {\r
2816         int index = TranslatePieceToFontPiece(piece);\r
2817 \r
2818         SelectObject( tmphdc, hPieceMask[ index ] );\r
2819 \r
2820       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2821         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2822       else\r
2823         BitBlt( hdc,\r
2824             x, y,\r
2825             squareSize, squareSize,\r
2826             tmphdc,\r
2827             0, 0,\r
2828             SRCAND );\r
2829 \r
2830         SelectObject( tmphdc, hPieceFace[ index ] );\r
2831 \r
2832       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2833         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2834       else\r
2835         BitBlt( hdc,\r
2836             x, y,\r
2837             squareSize, squareSize,\r
2838             tmphdc,\r
2839             0, 0,\r
2840             SRCPAINT );\r
2841 \r
2842         return;\r
2843     }\r
2844   }\r
2845 \r
2846   if (appData.monoMode) {\r
2847     SelectObject(tmphdc, PieceBitmap(piece, \r
2848       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2849     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2850            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2851   } else {\r
2852     tmpSize = squareSize;\r
2853     if(minorSize &&\r
2854         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2855          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2856       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2857       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2858       x += (squareSize - minorSize)>>1;\r
2859       y += squareSize - minorSize - 2;\r
2860       tmpSize = minorSize;\r
2861     }\r
2862     if (color || appData.allWhite ) {\r
2863       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2864       if( color )\r
2865               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2866       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2867       if(appData.upsideDown && color==flipView)\r
2868         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2869       else\r
2870         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2871       /* Use black for outline of white pieces */\r
2872       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2873       if(appData.upsideDown && color==flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2877     } else {\r
2878       /* Use square color for details of black pieces */\r
2879       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2880       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2881       if(appData.upsideDown && !flipView)\r
2882         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2883       else\r
2884         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2885     }\r
2886     SelectObject(hdc, oldBrush);\r
2887     SelectObject(tmphdc, oldBitmap);\r
2888   }\r
2889 }\r
2890 \r
2891 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2892 int GetBackTextureMode( int algo )\r
2893 {\r
2894     int result = BACK_TEXTURE_MODE_DISABLED;\r
2895 \r
2896     switch( algo ) \r
2897     {\r
2898         case BACK_TEXTURE_MODE_PLAIN:\r
2899             result = 1; /* Always use identity map */\r
2900             break;\r
2901         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2902             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2903             break;\r
2904     }\r
2905 \r
2906     return result;\r
2907 }\r
2908 \r
2909 /* \r
2910     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2911     to handle redraws cleanly (as random numbers would always be different).\r
2912 */\r
2913 VOID RebuildTextureSquareInfo()\r
2914 {\r
2915     BITMAP bi;\r
2916     int lite_w = 0;\r
2917     int lite_h = 0;\r
2918     int dark_w = 0;\r
2919     int dark_h = 0;\r
2920     int row;\r
2921     int col;\r
2922 \r
2923     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2924 \r
2925     if( liteBackTexture != NULL ) {\r
2926         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             lite_w = bi.bmWidth;\r
2928             lite_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     if( darkBackTexture != NULL ) {\r
2933         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2934             dark_w = bi.bmWidth;\r
2935             dark_h = bi.bmHeight;\r
2936         }\r
2937     }\r
2938 \r
2939     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2940         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2941             if( (col + row) & 1 ) {\r
2942                 /* Lite square */\r
2943                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2944                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2945                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2946                   else\r
2947                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2948                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2949                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2950                   else\r
2951                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2952                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2953                 }\r
2954             }\r
2955             else {\r
2956                 /* Dark square */\r
2957                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2958                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2959                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2960                   else\r
2961                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2962                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2963                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2964                   else\r
2965                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2966                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2967                 }\r
2968             }\r
2969         }\r
2970     }\r
2971 }\r
2972 \r
2973 /* [AS] Arrow highlighting support */\r
2974 \r
2975 static double A_WIDTH = 5; /* Width of arrow body */\r
2976 \r
2977 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2978 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2979 \r
2980 static double Sqr( double x )\r
2981 {\r
2982     return x*x;\r
2983 }\r
2984 \r
2985 static int Round( double x )\r
2986 {\r
2987     return (int) (x + 0.5);\r
2988 }\r
2989 \r
2990 /* Draw an arrow between two points using current settings */\r
2991 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2992 {\r
2993     POINT arrow[7];\r
2994     double dx, dy, j, k, x, y;\r
2995 \r
2996     if( d_x == s_x ) {\r
2997         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2998 \r
2999         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3000         arrow[0].y = s_y;\r
3001 \r
3002         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3003         arrow[1].y = d_y - h;\r
3004 \r
3005         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3006         arrow[2].y = d_y - h;\r
3007 \r
3008         arrow[3].x = d_x;\r
3009         arrow[3].y = d_y;\r
3010 \r
3011         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3012         arrow[5].y = d_y - h;\r
3013 \r
3014         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3015         arrow[4].y = d_y - h;\r
3016 \r
3017         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3018         arrow[6].y = s_y;\r
3019     }\r
3020     else if( d_y == s_y ) {\r
3021         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3022 \r
3023         arrow[0].x = s_x;\r
3024         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3025 \r
3026         arrow[1].x = d_x - w;\r
3027         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3028 \r
3029         arrow[2].x = d_x - w;\r
3030         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3031 \r
3032         arrow[3].x = d_x;\r
3033         arrow[3].y = d_y;\r
3034 \r
3035         arrow[5].x = d_x - w;\r
3036         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3037 \r
3038         arrow[4].x = d_x - w;\r
3039         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3040 \r
3041         arrow[6].x = s_x;\r
3042         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3043     }\r
3044     else {\r
3045         /* [AS] Needed a lot of paper for this! :-) */\r
3046         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3047         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3048   \r
3049         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3050 \r
3051         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3052 \r
3053         x = s_x;\r
3054         y = s_y;\r
3055 \r
3056         arrow[0].x = Round(x - j);\r
3057         arrow[0].y = Round(y + j*dx);\r
3058 \r
3059         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3060         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3061 \r
3062         if( d_x > s_x ) {\r
3063             x = (double) d_x - k;\r
3064             y = (double) d_y - k*dy;\r
3065         }\r
3066         else {\r
3067             x = (double) d_x + k;\r
3068             y = (double) d_y + k*dy;\r
3069         }\r
3070 \r
3071         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3072 \r
3073         arrow[6].x = Round(x - j);\r
3074         arrow[6].y = Round(y + j*dx);\r
3075 \r
3076         arrow[2].x = Round(arrow[6].x + 2*j);\r
3077         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3078 \r
3079         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3080         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3081 \r
3082         arrow[4].x = d_x;\r
3083         arrow[4].y = d_y;\r
3084 \r
3085         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3086         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3087     }\r
3088 \r
3089     Polygon( hdc, arrow, 7 );\r
3090 }\r
3091 \r
3092 /* [AS] Draw an arrow between two squares */\r
3093 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3094 {\r
3095     int s_x, s_y, d_x, d_y;\r
3096     HPEN hpen;\r
3097     HPEN holdpen;\r
3098     HBRUSH hbrush;\r
3099     HBRUSH holdbrush;\r
3100     LOGBRUSH stLB;\r
3101 \r
3102     if( s_col == d_col && s_row == d_row ) {\r
3103         return;\r
3104     }\r
3105 \r
3106     /* Get source and destination points */\r
3107     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3108     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3109 \r
3110     if( d_y > s_y ) {\r
3111         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3112     }\r
3113     else if( d_y < s_y ) {\r
3114         d_y += squareSize / 2 + squareSize / 4;\r
3115     }\r
3116     else {\r
3117         d_y += squareSize / 2;\r
3118     }\r
3119 \r
3120     if( d_x > s_x ) {\r
3121         d_x += squareSize / 2 - squareSize / 4;\r
3122     }\r
3123     else if( d_x < s_x ) {\r
3124         d_x += squareSize / 2 + squareSize / 4;\r
3125     }\r
3126     else {\r
3127         d_x += squareSize / 2;\r
3128     }\r
3129 \r
3130     s_x += squareSize / 2;\r
3131     s_y += squareSize / 2;\r
3132 \r
3133     /* Adjust width */\r
3134     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3135 \r
3136     /* Draw */\r
3137     stLB.lbStyle = BS_SOLID;\r
3138     stLB.lbColor = appData.highlightArrowColor;\r
3139     stLB.lbHatch = 0;\r
3140 \r
3141     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3142     holdpen = SelectObject( hdc, hpen );\r
3143     hbrush = CreateBrushIndirect( &stLB );\r
3144     holdbrush = SelectObject( hdc, hbrush );\r
3145 \r
3146     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3147 \r
3148     SelectObject( hdc, holdpen );\r
3149     SelectObject( hdc, holdbrush );\r
3150     DeleteObject( hpen );\r
3151     DeleteObject( hbrush );\r
3152 }\r
3153 \r
3154 BOOL HasHighlightInfo()\r
3155 {\r
3156     BOOL result = FALSE;\r
3157 \r
3158     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3159         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3160     {\r
3161         result = TRUE;\r
3162     }\r
3163 \r
3164     return result;\r
3165 }\r
3166 \r
3167 BOOL IsDrawArrowEnabled()\r
3168 {\r
3169     BOOL result = FALSE;\r
3170 \r
3171     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3172         result = TRUE;\r
3173     }\r
3174 \r
3175     return result;\r
3176 }\r
3177 \r
3178 VOID DrawArrowHighlight( HDC hdc )\r
3179 {\r
3180     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3181         DrawArrowBetweenSquares( hdc,\r
3182             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3183             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3184     }\r
3185 }\r
3186 \r
3187 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3188 {\r
3189     HRGN result = NULL;\r
3190 \r
3191     if( HasHighlightInfo() ) {\r
3192         int x1, y1, x2, y2;\r
3193         int sx, sy, dx, dy;\r
3194 \r
3195         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3196         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3197 \r
3198         sx = MIN( x1, x2 );\r
3199         sy = MIN( y1, y2 );\r
3200         dx = MAX( x1, x2 ) + squareSize;\r
3201         dy = MAX( y1, y2 ) + squareSize;\r
3202 \r
3203         result = CreateRectRgn( sx, sy, dx, dy );\r
3204     }\r
3205 \r
3206     return result;\r
3207 }\r
3208 \r
3209 /*\r
3210     Warning: this function modifies the behavior of several other functions. \r
3211     \r
3212     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3213     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3214     repaint is scattered all over the place, which is not good for features such as\r
3215     "arrow highlighting" that require a full repaint of the board.\r
3216 \r
3217     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3218     user interaction, when speed is not so important) but especially to avoid errors\r
3219     in the displayed graphics.\r
3220 \r
3221     In such patched places, I always try refer to this function so there is a single\r
3222     place to maintain knowledge.\r
3223     \r
3224     To restore the original behavior, just return FALSE unconditionally.\r
3225 */\r
3226 BOOL IsFullRepaintPreferrable()\r
3227 {\r
3228     BOOL result = FALSE;\r
3229 \r
3230     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3231         /* Arrow may appear on the board */\r
3232         result = TRUE;\r
3233     }\r
3234 \r
3235     return result;\r
3236 }\r
3237 \r
3238 /* \r
3239     This function is called by DrawPosition to know whether a full repaint must\r
3240     be forced or not.\r
3241 \r
3242     Only DrawPosition may directly call this function, which makes use of \r
3243     some state information. Other function should call DrawPosition specifying \r
3244     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3245 */\r
3246 BOOL DrawPositionNeedsFullRepaint()\r
3247 {\r
3248     BOOL result = FALSE;\r
3249 \r
3250     /* \r
3251         Probably a slightly better policy would be to trigger a full repaint\r
3252         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3253         but animation is fast enough that it's difficult to notice.\r
3254     */\r
3255     if( animInfo.piece == EmptySquare ) {\r
3256         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3257             result = TRUE;\r
3258         }\r
3259     }\r
3260 \r
3261     return result;\r
3262 }\r
3263 \r
3264 VOID\r
3265 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3266 {\r
3267   int row, column, x, y, square_color, piece_color;\r
3268   ChessSquare piece;\r
3269   HBRUSH oldBrush;\r
3270   HDC texture_hdc = NULL;\r
3271 \r
3272   /* [AS] Initialize background textures if needed */\r
3273   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3274       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3275       if( backTextureSquareSize != squareSize \r
3276        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3277           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3278           backTextureSquareSize = squareSize;\r
3279           RebuildTextureSquareInfo();\r
3280       }\r
3281 \r
3282       texture_hdc = CreateCompatibleDC( hdc );\r
3283   }\r
3284 \r
3285   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3286     for (column = 0; column < BOARD_WIDTH; column++) {\r
3287   \r
3288       SquareToPos(row, column, &x, &y);\r
3289 \r
3290       piece = board[row][column];\r
3291 \r
3292       square_color = ((column + row) % 2) == 1;\r
3293       if( gameInfo.variant == VariantXiangqi ) {\r
3294           square_color = !InPalace(row, column);\r
3295           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3296           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3297       }\r
3298       piece_color = (int) piece < (int) BlackPawn;\r
3299 \r
3300 \r
3301       /* [HGM] holdings file: light square or black */\r
3302       if(column == BOARD_LEFT-2) {\r
3303             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3307                 continue;\r
3308             }\r
3309       } else\r
3310       if(column == BOARD_RGHT + 1 ) {\r
3311             if( row < gameInfo.holdingsSize )\r
3312                 square_color = 1;\r
3313             else {\r
3314                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3315                 continue;\r
3316             }\r
3317       }\r
3318       if(column == BOARD_LEFT-1 ) /* left align */\r
3319             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3320       else if( column == BOARD_RGHT) /* right align */\r
3321             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3322       else\r
3323       if (appData.monoMode) {\r
3324         if (piece == EmptySquare) {\r
3325           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3326                  square_color ? WHITENESS : BLACKNESS);\r
3327         } else {\r
3328           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3329         }\r
3330       } \r
3331       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3332           /* [AS] Draw the square using a texture bitmap */\r
3333           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3334           int r = row, c = column; // [HGM] do not flip board in flipView\r
3335           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3336 \r
3337           DrawTile( x, y, \r
3338               squareSize, squareSize, \r
3339               hdc, \r
3340               texture_hdc,\r
3341               backTextureSquareInfo[r][c].mode,\r
3342               backTextureSquareInfo[r][c].x,\r
3343               backTextureSquareInfo[r][c].y );\r
3344 \r
3345           SelectObject( texture_hdc, hbm );\r
3346 \r
3347           if (piece != EmptySquare) {\r
3348               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3349           }\r
3350       }\r
3351       else {\r
3352         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3353 \r
3354         oldBrush = SelectObject(hdc, brush );\r
3355         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3356         SelectObject(hdc, oldBrush);\r
3357         if (piece != EmptySquare)\r
3358           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3359       }\r
3360     }\r
3361   }\r
3362 \r
3363   if( texture_hdc != NULL ) {\r
3364     DeleteDC( texture_hdc );\r
3365   }\r
3366 }\r
3367 \r
3368 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3369 void fputDW(FILE *f, int x)\r
3370 {\r
3371         fputc(x     & 255, f);\r
3372         fputc(x>>8  & 255, f);\r
3373         fputc(x>>16 & 255, f);\r
3374         fputc(x>>24 & 255, f);\r
3375 }\r
3376 \r
3377 #define MAX_CLIPS 200   /* more than enough */\r
3378 \r
3379 VOID\r
3380 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3381 {\r
3382 //  HBITMAP bufferBitmap;\r
3383   BITMAP bi;\r
3384 //  RECT Rect;\r
3385   HDC tmphdc;\r
3386   HBITMAP hbm;\r
3387   int w = 100, h = 50;\r
3388 \r
3389   if(logo == NULL) {\r
3390     if(!logoHeight) return;\r
3391     FillRect( hdc, &logoRect, whitePieceBrush );\r
3392   }\r
3393 //  GetClientRect(hwndMain, &Rect);\r
3394 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3395 //                                      Rect.bottom-Rect.top+1);\r
3396   tmphdc = CreateCompatibleDC(hdc);\r
3397   hbm = SelectObject(tmphdc, logo);\r
3398   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3399             w = bi.bmWidth;\r
3400             h = bi.bmHeight;\r
3401   }\r
3402   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3403                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3404   SelectObject(tmphdc, hbm);\r
3405   DeleteDC(tmphdc);\r
3406 }\r
3407 \r
3408 VOID\r
3409 DisplayLogos()\r
3410 {\r
3411   if(logoHeight) {\r
3412         HDC hdc = GetDC(hwndMain);\r
3413         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3414         if(appData.autoLogo) {\r
3415           \r
3416           switch(gameMode) { // pick logos based on game mode\r
3417             case IcsObserving:\r
3418                 whiteLogo = second.programLogo; // ICS logo\r
3419                 blackLogo = second.programLogo;\r
3420             default:\r
3421                 break;\r
3422             case IcsPlayingWhite:\r
3423                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3424                 blackLogo = second.programLogo; // ICS logo\r
3425                 break;\r
3426             case IcsPlayingBlack:\r
3427                 whiteLogo = second.programLogo; // ICS logo\r
3428                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3429                 break;\r
3430             case TwoMachinesPlay:\r
3431                 if(first.twoMachinesColor[0] == 'b') {\r
3432                     whiteLogo = second.programLogo;\r
3433                     blackLogo = first.programLogo;\r
3434                 }\r
3435                 break;\r
3436             case MachinePlaysWhite:\r
3437                 blackLogo = userLogo;\r
3438                 break;\r
3439             case MachinePlaysBlack:\r
3440                 whiteLogo = userLogo;\r
3441                 blackLogo = first.programLogo;\r
3442           }\r
3443         }\r
3444         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3445         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3446         ReleaseDC(hwndMain, hdc);\r
3447   }\r
3448 }\r
3449 \r
3450 void\r
3451 UpdateLogos(int display)\r
3452 { // called after loading new engine(s), in tourney or from menu\r
3453   LoadLogo(&first, 0, FALSE);\r
3454   LoadLogo(&second, 1, appData.icsActive);\r
3455   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3456   if(display) DisplayLogos();\r
3457 }\r
3458 \r
3459 static HDC hdcSeek;\r
3460 \r
3461 // [HGM] seekgraph\r
3462 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3463 {\r
3464     POINT stPt;\r
3465     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3466     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3467     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3468     SelectObject( hdcSeek, hp );\r
3469 }\r
3470 \r
3471 // front-end wrapper for drawing functions to do rectangles\r
3472 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3473 {\r
3474     HPEN hp;\r
3475     RECT rc;\r
3476 \r
3477     if (hdcSeek == NULL) {\r
3478     hdcSeek = GetDC(hwndMain);\r
3479       if (!appData.monoMode) {\r
3480         SelectPalette(hdcSeek, hPal, FALSE);\r
3481         RealizePalette(hdcSeek);\r
3482       }\r
3483     }\r
3484     hp = SelectObject( hdcSeek, gridPen );\r
3485     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3486     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3487     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3488     SelectObject( hdcSeek, hp );\r
3489 }\r
3490 \r
3491 // front-end wrapper for putting text in graph\r
3492 void DrawSeekText(char *buf, int x, int y)\r
3493 {\r
3494         SIZE stSize;\r
3495         SetBkMode( hdcSeek, TRANSPARENT );\r
3496         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3497         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3498 }\r
3499 \r
3500 void DrawSeekDot(int x, int y, int color)\r
3501 {\r
3502         int square = color & 0x80;\r
3503         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3504                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3505         color &= 0x7F;\r
3506         if(square)\r
3507             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3508                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3509         else\r
3510             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3511                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3512             SelectObject(hdcSeek, oldBrush);\r
3513 }\r
3514 \r
3515 VOID\r
3516 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3517 {\r
3518   static Board lastReq[2], lastDrawn[2];\r
3519   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3520   static int lastDrawnFlipView = 0;\r
3521   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3522   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3523   HDC tmphdc;\r
3524   HDC hdcmem;\r
3525   HBITMAP bufferBitmap;\r
3526   HBITMAP oldBitmap;\r
3527   RECT Rect;\r
3528   HRGN clips[MAX_CLIPS];\r
3529   ChessSquare dragged_piece = EmptySquare;\r
3530   int nr = twoBoards*partnerUp;\r
3531 \r
3532   /* I'm undecided on this - this function figures out whether a full\r
3533    * repaint is necessary on its own, so there's no real reason to have the\r
3534    * caller tell it that.  I think this can safely be set to FALSE - but\r
3535    * if we trust the callers not to request full repaints unnessesarily, then\r
3536    * we could skip some clipping work.  In other words, only request a full\r
3537    * redraw when the majority of pieces have changed positions (ie. flip, \r
3538    * gamestart and similar)  --Hawk\r
3539    */\r
3540   Boolean fullrepaint = repaint;\r
3541 \r
3542   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3543 \r
3544   if( DrawPositionNeedsFullRepaint() ) {\r
3545       fullrepaint = TRUE;\r
3546   }\r
3547 \r
3548   if (board == NULL) {\r
3549     if (!lastReqValid[nr]) {\r
3550       return;\r
3551     }\r
3552     board = lastReq[nr];\r
3553   } else {\r
3554     CopyBoard(lastReq[nr], board);\r
3555     lastReqValid[nr] = 1;\r
3556   }\r
3557 \r
3558   if (doingSizing) {\r
3559     return;\r
3560   }\r
3561 \r
3562   if (IsIconic(hwndMain)) {\r
3563     return;\r
3564   }\r
3565 \r
3566   if (hdc == NULL) {\r
3567     hdc = GetDC(hwndMain);\r
3568     if (!appData.monoMode) {\r
3569       SelectPalette(hdc, hPal, FALSE);\r
3570       RealizePalette(hdc);\r
3571     }\r
3572     releaseDC = TRUE;\r
3573   } else {\r
3574     releaseDC = FALSE;\r
3575   }\r
3576 \r
3577   /* Create some work-DCs */\r
3578   hdcmem = CreateCompatibleDC(hdc);\r
3579   tmphdc = CreateCompatibleDC(hdc);\r
3580 \r
3581   /* If dragging is in progress, we temporarely remove the piece */\r
3582   /* [HGM] or temporarily decrease count if stacked              */\r
3583   /*       !! Moved to before board compare !!                   */\r
3584   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3585     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3586     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3587             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3588         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3589     } else \r
3590     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3591             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3592         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3593     } else \r
3594         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3595   }\r
3596 \r
3597   /* Figure out which squares need updating by comparing the \r
3598    * newest board with the last drawn board and checking if\r
3599    * flipping has changed.\r
3600    */\r
3601   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3602     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3603       for (column = 0; column < BOARD_WIDTH; column++) {\r
3604         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3605           SquareToPos(row, column, &x, &y);\r
3606           clips[num_clips++] =\r
3607             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3608         }\r
3609       }\r
3610     }\r
3611    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3612     for (i=0; i<2; i++) {\r
3613       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3614           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3615         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3616             lastDrawnHighlight.sq[i].y >= 0) {\r
3617           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3618                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3619           clips[num_clips++] =\r
3620             CreateRectRgn(x - lineGap, y - lineGap, \r
3621                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3622         }\r
3623         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3624           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3625           clips[num_clips++] =\r
3626             CreateRectRgn(x - lineGap, y - lineGap, \r
3627                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3628         }\r
3629       }\r
3630     }\r
3631     for (i=0; i<2; i++) {\r
3632       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3633           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3634         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3635             lastDrawnPremove.sq[i].y >= 0) {\r
3636           SquareToPos(lastDrawnPremove.sq[i].y,\r
3637                       lastDrawnPremove.sq[i].x, &x, &y);\r
3638           clips[num_clips++] =\r
3639             CreateRectRgn(x - lineGap, y - lineGap, \r
3640                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3641         }\r
3642         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3643             premoveHighlightInfo.sq[i].y >= 0) {\r
3644           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3645                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3646           clips[num_clips++] =\r
3647             CreateRectRgn(x - lineGap, y - lineGap, \r
3648                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3649         }\r
3650       }\r
3651     }\r
3652    } else { // nr == 1\r
3653         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3654         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3655         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3656         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3657       for (i=0; i<2; i++) {\r
3658         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3659             partnerHighlightInfo.sq[i].y >= 0) {\r
3660           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3661                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3662           clips[num_clips++] =\r
3663             CreateRectRgn(x - lineGap, y - lineGap, \r
3664                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3665         }\r
3666         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3667             oldPartnerHighlight.sq[i].y >= 0) {\r
3668           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3669                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3670           clips[num_clips++] =\r
3671             CreateRectRgn(x - lineGap, y - lineGap, \r
3672                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3673         }\r
3674       }\r
3675    }\r
3676   } else {\r
3677     fullrepaint = TRUE;\r
3678   }\r
3679 \r
3680   /* Create a buffer bitmap - this is the actual bitmap\r
3681    * being written to.  When all the work is done, we can\r
3682    * copy it to the real DC (the screen).  This avoids\r
3683    * the problems with flickering.\r
3684    */\r
3685   GetClientRect(hwndMain, &Rect);\r
3686   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3687                                         Rect.bottom-Rect.top+1);\r
3688   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3689   if (!appData.monoMode) {\r
3690     SelectPalette(hdcmem, hPal, FALSE);\r
3691   }\r
3692 \r
3693   /* Create clips for dragging */\r
3694   if (!fullrepaint) {\r
3695     if (dragInfo.from.x >= 0) {\r
3696       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3697       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3698     }\r
3699     if (dragInfo.start.x >= 0) {\r
3700       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3701       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3702     }\r
3703     if (dragInfo.pos.x >= 0) {\r
3704       x = dragInfo.pos.x - squareSize / 2;\r
3705       y = dragInfo.pos.y - squareSize / 2;\r
3706       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3707     }\r
3708     if (dragInfo.lastpos.x >= 0) {\r
3709       x = dragInfo.lastpos.x - squareSize / 2;\r
3710       y = dragInfo.lastpos.y - squareSize / 2;\r
3711       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3712     }\r
3713   }\r
3714 \r
3715   /* Are we animating a move?  \r
3716    * If so, \r
3717    *   - remove the piece from the board (temporarely)\r
3718    *   - calculate the clipping region\r
3719    */\r
3720   if (!fullrepaint) {\r
3721     if (animInfo.piece != EmptySquare) {\r
3722       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3723       x = boardRect.left + animInfo.lastpos.x;\r
3724       y = boardRect.top + animInfo.lastpos.y;\r
3725       x2 = boardRect.left + animInfo.pos.x;\r
3726       y2 = boardRect.top + animInfo.pos.y;\r
3727       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3728       /* Slight kludge.  The real problem is that after AnimateMove is\r
3729          done, the position on the screen does not match lastDrawn.\r
3730          This currently causes trouble only on e.p. captures in\r
3731          atomic, where the piece moves to an empty square and then\r
3732          explodes.  The old and new positions both had an empty square\r
3733          at the destination, but animation has drawn a piece there and\r
3734          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3735       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3736     }\r
3737   }\r
3738 \r
3739   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3740   if (num_clips == 0)\r
3741     fullrepaint = TRUE;\r
3742 \r
3743   /* Set clipping on the memory DC */\r
3744   if (!fullrepaint) {\r
3745     SelectClipRgn(hdcmem, clips[0]);\r
3746     for (x = 1; x < num_clips; x++) {\r
3747       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3748         abort();  // this should never ever happen!\r
3749     }\r
3750   }\r
3751 \r
3752   /* Do all the drawing to the memory DC */\r
3753   if(explodeInfo.radius) { // [HGM] atomic\r
3754         HBRUSH oldBrush;\r
3755         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3756         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3757         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3758         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3759         x += squareSize/2;\r
3760         y += squareSize/2;\r
3761         if(!fullrepaint) {\r
3762           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3763           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3764         }\r
3765         DrawGridOnDC(hdcmem);\r
3766         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3767         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3768         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3769         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3770         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3771         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3772         SelectObject(hdcmem, oldBrush);\r
3773   } else {\r
3774     DrawGridOnDC(hdcmem);\r
3775     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3776         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3777         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3778     } else {\r
3779         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3780         oldPartnerHighlight = partnerHighlightInfo;\r
3781     }\r
3782     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3783   }\r
3784   if(nr == 0) // [HGM] dual: markers only on left board\r
3785   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3786     for (column = 0; column < BOARD_WIDTH; column++) {\r
3787         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3788             HBRUSH oldBrush = SelectObject(hdcmem, \r
3789                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3790             SquareToPos(row, column, &x, &y);\r
3791             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3792                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3793             SelectObject(hdcmem, oldBrush);\r
3794         }\r
3795     }\r
3796   }\r
3797 \r
3798   if( appData.highlightMoveWithArrow ) {\r
3799     DrawArrowHighlight(hdcmem);\r
3800   }\r
3801 \r
3802   DrawCoordsOnDC(hdcmem);\r
3803 \r
3804   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3805                  /* to make sure lastDrawn contains what is actually drawn */\r
3806 \r
3807   /* Put the dragged piece back into place and draw it (out of place!) */\r
3808     if (dragged_piece != EmptySquare) {\r
3809     /* [HGM] or restack */\r
3810     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3811                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3812     else\r
3813     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3814                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3815     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3816     x = dragInfo.pos.x - squareSize / 2;\r
3817     y = dragInfo.pos.y - squareSize / 2;\r
3818     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3819                   ((int) dragInfo.piece < (int) BlackPawn), \r
3820                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3821   }   \r
3822   \r
3823   /* Put the animated piece back into place and draw it */\r
3824   if (animInfo.piece != EmptySquare) {\r
3825     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3826     x = boardRect.left + animInfo.pos.x;\r
3827     y = boardRect.top + animInfo.pos.y;\r
3828     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3829                   ((int) animInfo.piece < (int) BlackPawn),\r
3830                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3831   }\r
3832 \r
3833   /* Release the bufferBitmap by selecting in the old bitmap \r
3834    * and delete the memory DC\r
3835    */\r
3836   SelectObject(hdcmem, oldBitmap);\r
3837   DeleteDC(hdcmem);\r
3838 \r
3839   /* Set clipping on the target DC */\r
3840   if (!fullrepaint) {\r
3841     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3842         RECT rect;\r
3843         GetRgnBox(clips[x], &rect);\r
3844         DeleteObject(clips[x]);\r
3845         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3846                           rect.right + wpMain.width/2, rect.bottom);\r
3847     }\r
3848     SelectClipRgn(hdc, clips[0]);\r
3849     for (x = 1; x < num_clips; x++) {\r
3850       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3851         abort();   // this should never ever happen!\r
3852     } \r
3853   }\r
3854 \r
3855   /* Copy the new bitmap onto the screen in one go.\r
3856    * This way we avoid any flickering\r
3857    */\r
3858   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3859   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3860          boardRect.right - boardRect.left,\r
3861          boardRect.bottom - boardRect.top,\r
3862          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3863   if(saveDiagFlag) { \r
3864     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3865     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3866 \r
3867     GetObject(bufferBitmap, sizeof(b), &b);\r
3868     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3869         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3870         bih.biWidth = b.bmWidth;\r
3871         bih.biHeight = b.bmHeight;\r
3872         bih.biPlanes = 1;\r
3873         bih.biBitCount = b.bmBitsPixel;\r
3874         bih.biCompression = 0;\r
3875         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3876         bih.biXPelsPerMeter = 0;\r
3877         bih.biYPelsPerMeter = 0;\r
3878         bih.biClrUsed = 0;\r
3879         bih.biClrImportant = 0;\r
3880 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3881 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3882         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3883 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3884 \r
3885         wb = b.bmWidthBytes;\r
3886         // count colors\r
3887         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3888                 int k = ((int*) pData)[i];\r
3889                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3890                 if(j >= 16) break;\r
3891                 color[j] = k;\r
3892                 if(j >= nrColors) nrColors = j+1;\r
3893         }\r
3894         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3895                 INT p = 0;\r
3896                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3897                     for(w=0; w<(wb>>2); w+=2) {\r
3898                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3899                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3900                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3901                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3902                         pData[p++] = m | j<<4;\r
3903                     }\r
3904                     while(p&3) pData[p++] = 0;\r
3905                 }\r
3906                 fac = 3;\r
3907                 wb = ((wb+31)>>5)<<2;\r
3908         }\r
3909         // write BITMAPFILEHEADER\r
3910         fprintf(diagFile, "BM");\r
3911         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3912         fputDW(diagFile, 0);\r
3913         fputDW(diagFile, 0x36 + (fac?64:0));\r
3914         // write BITMAPINFOHEADER\r
3915         fputDW(diagFile, 40);\r
3916         fputDW(diagFile, b.bmWidth);\r
3917         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3918         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3919         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3920         fputDW(diagFile, 0);\r
3921         fputDW(diagFile, 0);\r
3922         fputDW(diagFile, 0);\r
3923         fputDW(diagFile, 0);\r
3924         fputDW(diagFile, 0);\r
3925         fputDW(diagFile, 0);\r
3926         // write color table\r
3927         if(fac)\r
3928         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3929         // write bitmap data\r
3930         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3931                 fputc(pData[i], diagFile);\r
3932         free(pData);\r
3933      }\r
3934   }\r
3935 \r
3936   SelectObject(tmphdc, oldBitmap);\r
3937 \r
3938   /* Massive cleanup */\r
3939   for (x = 0; x < num_clips; x++)\r
3940     DeleteObject(clips[x]);\r
3941 \r
3942   DeleteDC(tmphdc);\r
3943   DeleteObject(bufferBitmap);\r
3944 \r
3945   if (releaseDC) \r
3946     ReleaseDC(hwndMain, hdc);\r
3947   \r
3948   if (lastDrawnFlipView != flipView && nr == 0) {\r
3949     if (flipView)\r
3950       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3951     else\r
3952       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3953   }\r
3954 \r
3955 /*  CopyBoard(lastDrawn, board);*/\r
3956   lastDrawnHighlight = highlightInfo;\r
3957   lastDrawnPremove   = premoveHighlightInfo;\r
3958   lastDrawnFlipView = flipView;\r
3959   lastDrawnValid[nr] = 1;\r
3960 }\r
3961 \r
3962 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3963 int\r
3964 SaveDiagram(f)\r
3965      FILE *f;\r
3966 {\r
3967     saveDiagFlag = 1; diagFile = f;\r
3968     HDCDrawPosition(NULL, TRUE, NULL);\r
3969     saveDiagFlag = 0;\r
3970 \r
3971     fclose(f);\r
3972     return TRUE;\r
3973 }\r
3974 \r
3975 \r
3976 /*---------------------------------------------------------------------------*\\r
3977 | CLIENT PAINT PROCEDURE\r
3978 |   This is the main event-handler for the WM_PAINT message.\r
3979 |\r
3980 \*---------------------------------------------------------------------------*/\r
3981 VOID\r
3982 PaintProc(HWND hwnd)\r
3983 {\r
3984   HDC         hdc;\r
3985   PAINTSTRUCT ps;\r
3986   HFONT       oldFont;\r
3987 \r
3988   if((hdc = BeginPaint(hwnd, &ps))) {\r
3989     if (IsIconic(hwnd)) {\r
3990       DrawIcon(hdc, 2, 2, iconCurrent);\r
3991     } else {\r
3992       if (!appData.monoMode) {\r
3993         SelectPalette(hdc, hPal, FALSE);\r
3994         RealizePalette(hdc);\r
3995       }\r
3996       HDCDrawPosition(hdc, 1, NULL);\r
3997       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3998         flipView = !flipView; partnerUp = !partnerUp;\r
3999         HDCDrawPosition(hdc, 1, NULL);\r
4000         flipView = !flipView; partnerUp = !partnerUp;\r
4001       }\r
4002       oldFont =\r
4003         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4004       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4005                  ETO_CLIPPED|ETO_OPAQUE,\r
4006                  &messageRect, messageText, strlen(messageText), NULL);\r
4007       SelectObject(hdc, oldFont);\r
4008       DisplayBothClocks();\r
4009       DisplayLogos();\r
4010     }\r
4011     EndPaint(hwnd,&ps);\r
4012   }\r
4013 \r
4014   return;\r
4015 }\r
4016 \r
4017 \r
4018 /*\r
4019  * If the user selects on a border boundary, return -1; if off the board,\r
4020  *   return -2.  Otherwise map the event coordinate to the square.\r
4021  * The offset boardRect.left or boardRect.top must already have been\r
4022  *   subtracted from x.\r
4023  */\r
4024 int EventToSquare(x, limit)\r
4025      int x, limit;\r
4026 {\r
4027   if (x <= 0)\r
4028     return -2;\r
4029   if (x < lineGap)\r
4030     return -1;\r
4031   x -= lineGap;\r
4032   if ((x % (squareSize + lineGap)) >= squareSize)\r
4033     return -1;\r
4034   x /= (squareSize + lineGap);\r
4035     if (x >= limit)\r
4036     return -2;\r
4037   return x;\r
4038 }\r
4039 \r
4040 typedef struct {\r
4041   char piece;\r
4042   int command;\r
4043   char* name;\r
4044 } DropEnable;\r
4045 \r
4046 DropEnable dropEnables[] = {\r
4047   { 'P', DP_Pawn, N_("Pawn") },\r
4048   { 'N', DP_Knight, N_("Knight") },\r
4049   { 'B', DP_Bishop, N_("Bishop") },\r
4050   { 'R', DP_Rook, N_("Rook") },\r
4051   { 'Q', DP_Queen, N_("Queen") },\r
4052 };\r
4053 \r
4054 VOID\r
4055 SetupDropMenu(HMENU hmenu)\r
4056 {\r
4057   int i, count, enable;\r
4058   char *p;\r
4059   extern char white_holding[], black_holding[];\r
4060   char item[MSG_SIZ];\r
4061 \r
4062   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4063     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4064                dropEnables[i].piece);\r
4065     count = 0;\r
4066     while (p && *p++ == dropEnables[i].piece) count++;\r
4067       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4068     enable = count > 0 || !appData.testLegality\r
4069       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4070                       && !appData.icsActive);\r
4071     ModifyMenu(hmenu, dropEnables[i].command,\r
4072                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4073                dropEnables[i].command, item);\r
4074   }\r
4075 }\r
4076 \r
4077 void DragPieceBegin(int x, int y, Boolean instantly)\r
4078 {\r
4079       dragInfo.lastpos.x = boardRect.left + x;\r
4080       dragInfo.lastpos.y = boardRect.top + y;\r
4081       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4082       dragInfo.from.x = fromX;\r
4083       dragInfo.from.y = fromY;\r
4084       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4085       dragInfo.start = dragInfo.from;\r
4086       SetCapture(hwndMain);\r
4087 }\r
4088 \r
4089 void DragPieceEnd(int x, int y)\r
4090 {\r
4091     ReleaseCapture();\r
4092     dragInfo.start.x = dragInfo.start.y = -1;\r
4093     dragInfo.from = dragInfo.start;\r
4094     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4095 }\r
4096 \r
4097 void ChangeDragPiece(ChessSquare piece)\r
4098 {\r
4099     dragInfo.piece = piece;\r
4100 }\r
4101 \r
4102 /* Event handler for mouse messages */\r
4103 VOID\r
4104 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4105 {\r
4106   int x, y, menuNr;\r
4107   POINT pt;\r
4108   static int recursive = 0;\r
4109   HMENU hmenu;\r
4110   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4111 \r
4112   if (recursive) {\r
4113     if (message == WM_MBUTTONUP) {\r
4114       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4115          to the middle button: we simulate pressing the left button too!\r
4116          */\r
4117       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4118       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4119     }\r
4120     return;\r
4121   }\r
4122   recursive++;\r
4123   \r
4124   pt.x = LOWORD(lParam);\r
4125   pt.y = HIWORD(lParam);\r
4126   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4127   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4128   if (!flipView && y >= 0) {\r
4129     y = BOARD_HEIGHT - 1 - y;\r
4130   }\r
4131   if (flipView && x >= 0) {\r
4132     x = BOARD_WIDTH - 1 - x;\r
4133   }\r
4134 \r
4135   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4136 \r
4137   switch (message) {\r
4138   case WM_LBUTTONDOWN:\r
4139       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4140         ClockClick(flipClock); break;\r
4141       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4142         ClockClick(!flipClock); break;\r
4143       }\r
4144       dragInfo.start.x = dragInfo.start.y = -1;\r
4145       dragInfo.from = dragInfo.start;\r
4146     if(fromX == -1 && frozen) { // not sure where this is for\r
4147                 fromX = fromY = -1; \r
4148       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4149       break;\r
4150     }\r
4151       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4152       DrawPosition(TRUE, NULL);\r
4153     break;\r
4154 \r
4155   case WM_LBUTTONUP:\r
4156       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4157       DrawPosition(TRUE, NULL);\r
4158     break;\r
4159 \r
4160   case WM_MOUSEMOVE:\r
4161     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4162     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4163     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4164     if ((appData.animateDragging || appData.highlightDragging)\r
4165         && (wParam & MK_LBUTTON)\r
4166         && dragInfo.from.x >= 0) \r
4167     {\r
4168       BOOL full_repaint = FALSE;\r
4169 \r
4170       if (appData.animateDragging) {\r
4171         dragInfo.pos = pt;\r
4172       }\r
4173       if (appData.highlightDragging) {\r
4174         SetHighlights(fromX, fromY, x, y);\r
4175         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4176             full_repaint = TRUE;\r
4177         }\r
4178       }\r
4179       \r
4180       DrawPosition( full_repaint, NULL);\r
4181       \r
4182       dragInfo.lastpos = dragInfo.pos;\r
4183     }\r
4184     break;\r
4185 \r
4186   case WM_MOUSEWHEEL: // [DM]\r
4187     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4188        /* Mouse Wheel is being rolled forward\r
4189         * Play moves forward\r
4190         */\r
4191        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4192                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4193        /* Mouse Wheel is being rolled backward\r
4194         * Play moves backward\r
4195         */\r
4196        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4197                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4198     }\r
4199     break;\r
4200 \r
4201   case WM_MBUTTONUP:\r
4202   case WM_RBUTTONUP:\r
4203     ReleaseCapture();\r
4204     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4205     break;\r
4206  \r
4207   case WM_MBUTTONDOWN:\r
4208   case WM_RBUTTONDOWN:\r
4209     ErrorPopDown();\r
4210     ReleaseCapture();\r
4211     fromX = fromY = -1;\r
4212     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4213     dragInfo.start.x = dragInfo.start.y = -1;\r
4214     dragInfo.from = dragInfo.start;\r
4215     dragInfo.lastpos = dragInfo.pos;\r
4216     if (appData.highlightDragging) {\r
4217       ClearHighlights();\r
4218     }\r
4219     if(y == -2) {\r
4220       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4221       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4222           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4223       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4224           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4225       }\r
4226       break;\r
4227     }\r
4228     DrawPosition(TRUE, NULL);\r
4229 \r
4230     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4231     switch (menuNr) {\r
4232     case 0:\r
4233       if (message == WM_MBUTTONDOWN) {\r
4234         buttonCount = 3;  /* even if system didn't think so */\r
4235         if (wParam & MK_SHIFT) \r
4236           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4237         else\r
4238           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4239       } else { /* message == WM_RBUTTONDOWN */\r
4240         /* Just have one menu, on the right button.  Windows users don't\r
4241            think to try the middle one, and sometimes other software steals\r
4242            it, or it doesn't really exist. */\r
4243         if(gameInfo.variant != VariantShogi)\r
4244             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4245         else\r
4246             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4247       }\r
4248       break;\r
4249     case 2:\r
4250       SetCapture(hwndMain);
4251       break;\r
4252     case 1:\r
4253       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4254       SetupDropMenu(hmenu);\r
4255       MenuPopup(hwnd, pt, hmenu, -1);\r
4256     default:\r
4257       break;\r
4258     }\r
4259     break;\r
4260   }\r
4261 \r
4262   recursive--;\r
4263 }\r
4264 \r
4265 /* Preprocess messages for buttons in main window */\r
4266 LRESULT CALLBACK\r
4267 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4268 {\r
4269   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4270   int i, dir;\r
4271 \r
4272   for (i=0; i<N_BUTTONS; i++) {\r
4273     if (buttonDesc[i].id == id) break;\r
4274   }\r
4275   if (i == N_BUTTONS) return 0;\r
4276   switch (message) {\r
4277   case WM_KEYDOWN:\r
4278     switch (wParam) {\r
4279     case VK_LEFT:\r
4280     case VK_RIGHT:\r
4281       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4282       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4283       return TRUE;\r
4284     }\r
4285     break;\r
4286   case WM_CHAR:\r
4287     switch (wParam) {\r
4288     case '\r':\r
4289       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4290       return TRUE;\r
4291     default:\r
4292       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4293         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4294         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4295         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4296         SetFocus(h);\r
4297         SendMessage(h, WM_CHAR, wParam, lParam);\r
4298         return TRUE;\r
4299       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4300         TypeInEvent((char)wParam);\r
4301       }\r
4302       break;\r
4303     }\r
4304     break;\r
4305   }\r
4306   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4307 }\r
4308 \r
4309 /* Process messages for Promotion dialog box */\r
4310 LRESULT CALLBACK\r
4311 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4312 {\r
4313   char promoChar;\r
4314 \r
4315   switch (message) {\r
4316   case WM_INITDIALOG: /* message: initialize dialog box */\r
4317     /* Center the dialog over the application window */\r
4318     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4319     Translate(hDlg, DLG_PromotionKing);\r
4320     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4321       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4322        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4323        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4324                SW_SHOW : SW_HIDE);\r
4325     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4326     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4327        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4328          PieceToChar(WhiteAngel) != '~') ||\r
4329         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4330          PieceToChar(BlackAngel) != '~')   ) ?\r
4331                SW_SHOW : SW_HIDE);\r
4332     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4333        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4334          PieceToChar(WhiteMarshall) != '~') ||\r
4335         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4336          PieceToChar(BlackMarshall) != '~')   ) ?\r
4337                SW_SHOW : SW_HIDE);\r
4338     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4339     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4340        gameInfo.variant != VariantShogi ?\r
4341                SW_SHOW : SW_HIDE);\r
4342     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4343        gameInfo.variant != VariantShogi ?\r
4344                SW_SHOW : SW_HIDE);\r
4345     if(gameInfo.variant == VariantShogi) {\r
4346         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4347         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4348         SetWindowText(hDlg, "Promote?");\r
4349     }\r
4350     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4351        gameInfo.variant == VariantSuper ?\r
4352                SW_SHOW : SW_HIDE);\r
4353     return TRUE;\r
4354 \r
4355   case WM_COMMAND: /* message: received a command */\r
4356     switch (LOWORD(wParam)) {\r
4357     case IDCANCEL:\r
4358       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4359       ClearHighlights();\r
4360       DrawPosition(FALSE, NULL);\r
4361       return TRUE;\r
4362     case PB_King:\r
4363       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4364       break;\r
4365     case PB_Queen:\r
4366       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4367       break;\r
4368     case PB_Rook:\r
4369       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4370       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4371       break;\r
4372     case PB_Bishop:\r
4373       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4374       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4375       break;\r
4376     case PB_Chancellor:\r
4377       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4378       break;\r
4379     case PB_Archbishop:\r
4380       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4381       break;\r
4382     case PB_Knight:\r
4383       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4384       break;\r
4385     default:\r
4386       return FALSE;\r
4387     }\r
4388     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4389     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4390     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4391     fromX = fromY = -1;\r
4392     if (!appData.highlightLastMove) {\r
4393       ClearHighlights();\r
4394       DrawPosition(FALSE, NULL);\r
4395     }\r
4396     return TRUE;\r
4397   }\r
4398   return FALSE;\r
4399 }\r
4400 \r
4401 /* Pop up promotion dialog */\r
4402 VOID\r
4403 PromotionPopup(HWND hwnd)\r
4404 {\r
4405   FARPROC lpProc;\r
4406 \r
4407   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4408   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4409     hwnd, (DLGPROC)lpProc);\r
4410   FreeProcInstance(lpProc);\r
4411 }\r
4412 \r
4413 void\r
4414 PromotionPopUp()\r
4415 {\r
4416   DrawPosition(TRUE, NULL);\r
4417   PromotionPopup(hwndMain);\r
4418 }\r
4419 \r
4420 /* Toggle ShowThinking */\r
4421 VOID\r
4422 ToggleShowThinking()\r
4423 {\r
4424   appData.showThinking = !appData.showThinking;\r
4425   ShowThinkingEvent();\r
4426 }\r
4427 \r
4428 VOID\r
4429 LoadGameDialog(HWND hwnd, char* title)\r
4430 {\r
4431   UINT number = 0;\r
4432   FILE *f;\r
4433   char fileTitle[MSG_SIZ];\r
4434   f = OpenFileDialog(hwnd, "rb", "",\r
4435                      appData.oldSaveStyle ? "gam" : "pgn",\r
4436                      GAME_FILT,\r
4437                      title, &number, fileTitle, NULL);\r
4438   if (f != NULL) {\r
4439     cmailMsgLoaded = FALSE;\r
4440     if (number == 0) {\r
4441       int error = GameListBuild(f);\r
4442       if (error) {\r
4443         DisplayError(_("Cannot build game list"), error);\r
4444       } else if (!ListEmpty(&gameList) &&\r
4445                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4446         GameListPopUp(f, fileTitle);\r
4447         return;\r
4448       }\r
4449       GameListDestroy();\r
4450       number = 1;\r
4451     }\r
4452     LoadGame(f, number, fileTitle, FALSE);\r
4453   }\r
4454 }\r
4455 \r
4456 int get_term_width()\r
4457 {\r
4458     HDC hdc;\r
4459     TEXTMETRIC tm;\r
4460     RECT rc;\r
4461     HFONT hfont, hold_font;\r
4462     LOGFONT lf;\r
4463     HWND hText;\r
4464 \r
4465     if (hwndConsole)\r
4466         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4467     else\r
4468         return 79;\r
4469 \r
4470     // get the text metrics\r
4471     hdc = GetDC(hText);\r
4472     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4473     if (consoleCF.dwEffects & CFE_BOLD)\r
4474         lf.lfWeight = FW_BOLD;\r
4475     if (consoleCF.dwEffects & CFE_ITALIC)\r
4476         lf.lfItalic = TRUE;\r
4477     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4478         lf.lfStrikeOut = TRUE;\r
4479     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4480         lf.lfUnderline = TRUE;\r
4481     hfont = CreateFontIndirect(&lf);\r
4482     hold_font = SelectObject(hdc, hfont);\r
4483     GetTextMetrics(hdc, &tm);\r
4484     SelectObject(hdc, hold_font);\r
4485     DeleteObject(hfont);\r
4486     ReleaseDC(hText, hdc);\r
4487 \r
4488     // get the rectangle\r
4489     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4490 \r
4491     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4492 }\r
4493 \r
4494 void UpdateICSWidth(HWND hText)\r
4495 {\r
4496     LONG old_width, new_width;\r
4497 \r
4498     new_width = get_term_width(hText, FALSE);\r
4499     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4500     if (new_width != old_width)\r
4501     {\r
4502         ics_update_width(new_width);\r
4503         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4504     }\r
4505 }\r
4506 \r
4507 VOID\r
4508 ChangedConsoleFont()\r
4509 {\r
4510   CHARFORMAT cfmt;\r
4511   CHARRANGE tmpsel, sel;\r
4512   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4513   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4514   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4515   PARAFORMAT paraf;\r
4516 \r
4517   cfmt.cbSize = sizeof(CHARFORMAT);\r
4518   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4519     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4520                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4521   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4522    * size.  This was undocumented in the version of MSVC++ that I had\r
4523    * when I wrote the code, but is apparently documented now.\r
4524    */\r
4525   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4526   cfmt.bCharSet = f->lf.lfCharSet;\r
4527   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4528   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4529   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4530   /* Why are the following seemingly needed too? */\r
4531   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4532   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4533   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4534   tmpsel.cpMin = 0;\r
4535   tmpsel.cpMax = -1; /*999999?*/\r
4536   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4537   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4538   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4539    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4540    */\r
4541   paraf.cbSize = sizeof(paraf);\r
4542   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4543   paraf.dxStartIndent = 0;\r
4544   paraf.dxOffset = WRAP_INDENT;\r
4545   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4546   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4547   UpdateICSWidth(hText);\r
4548 }\r
4549 \r
4550 /*---------------------------------------------------------------------------*\\r
4551  *\r
4552  * Window Proc for main window\r
4553  *\r
4554 \*---------------------------------------------------------------------------*/\r
4555 \r
4556 /* Process messages for main window, etc. */\r
4557 LRESULT CALLBACK\r
4558 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4559 {\r
4560   FARPROC lpProc;\r
4561   int wmId, wmEvent;\r
4562   char *defName;\r
4563   FILE *f;\r
4564   UINT number;\r
4565   char fileTitle[MSG_SIZ];\r
4566   char buf[MSG_SIZ];\r
4567   static SnapData sd;\r
4568   static int peek=0;\r
4569 \r
4570   switch (message) {\r
4571 \r
4572   case WM_PAINT: /* message: repaint portion of window */\r
4573     PaintProc(hwnd);\r
4574     break;\r
4575 \r
4576   case WM_ERASEBKGND:\r
4577     if (IsIconic(hwnd)) {\r
4578       /* Cheat; change the message */\r
4579       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4580     } else {\r
4581       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4582     }\r
4583     break;\r
4584 \r
4585   case WM_LBUTTONDOWN:\r
4586   case WM_MBUTTONDOWN:\r
4587   case WM_RBUTTONDOWN:\r
4588   case WM_LBUTTONUP:\r
4589   case WM_MBUTTONUP:\r
4590   case WM_RBUTTONUP:\r
4591   case WM_MOUSEMOVE:\r
4592   case WM_MOUSEWHEEL:\r
4593     MouseEvent(hwnd, message, wParam, lParam);\r
4594     break;\r
4595 \r
4596   case WM_KEYUP:\r
4597     if((char)wParam == '\b') {\r
4598       ForwardEvent(); peek = 0;\r
4599     }\r
4600 \r
4601     JAWS_KBUP_NAVIGATION\r
4602 \r
4603     break;\r
4604 \r
4605   case WM_KEYDOWN:\r
4606     if((char)wParam == '\b') {\r
4607       if(!peek) BackwardEvent(), peek = 1;\r
4608     }\r
4609 \r
4610     JAWS_KBDOWN_NAVIGATION\r
4611 \r
4612     break;\r
4613 \r
4614   case WM_CHAR:\r
4615     \r
4616     JAWS_ALT_INTERCEPT\r
4617 \r
4618     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4619         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4620         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4621         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4622         SetFocus(h);\r
4623         SendMessage(h, message, wParam, lParam);\r
4624     } else if(lParam != KF_REPEAT) {\r
4625         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4626                 TypeInEvent((char)wParam);\r
4627         } else if((char)wParam == 003) CopyGameToClipboard();\r
4628          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4629     }\r
4630 \r
4631     break;\r
4632 \r
4633   case WM_PALETTECHANGED:\r
4634     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4635       int nnew;\r
4636       HDC hdc = GetDC(hwndMain);\r
4637       SelectPalette(hdc, hPal, TRUE);\r
4638       nnew = RealizePalette(hdc);\r
4639       if (nnew > 0) {\r
4640         paletteChanged = TRUE;\r
4641         InvalidateRect(hwnd, &boardRect, FALSE);\r
4642       }\r
4643       ReleaseDC(hwnd, hdc);\r
4644     }\r
4645     break;\r
4646 \r
4647   case WM_QUERYNEWPALETTE:\r
4648     if (!appData.monoMode /*&& paletteChanged*/) {\r
4649       int nnew;\r
4650       HDC hdc = GetDC(hwndMain);\r
4651       paletteChanged = FALSE;\r
4652       SelectPalette(hdc, hPal, FALSE);\r
4653       nnew = RealizePalette(hdc);\r
4654       if (nnew > 0) {\r
4655         InvalidateRect(hwnd, &boardRect, FALSE);\r
4656       }\r
4657       ReleaseDC(hwnd, hdc);\r
4658       return TRUE;\r
4659     }\r
4660     return FALSE;\r
4661 \r
4662   case WM_COMMAND: /* message: command from application menu */\r
4663     wmId    = LOWORD(wParam);\r
4664     wmEvent = HIWORD(wParam);\r
4665 \r
4666     switch (wmId) {\r
4667     case IDM_NewGame:\r
4668       ResetGameEvent();\r
4669       SAY("new game enter a move to play against the computer with white");\r
4670       break;\r
4671 \r
4672     case IDM_NewGameFRC:\r
4673       if( NewGameFRC() == 0 ) {\r
4674         ResetGameEvent();\r
4675       }\r
4676       break;\r
4677 \r
4678     case IDM_NewVariant:\r
4679       NewVariantPopup(hwnd);\r
4680       break;\r
4681 \r
4682     case IDM_LoadGame:\r
4683       LoadGameDialog(hwnd, _("Load Game from File"));\r
4684       break;\r
4685 \r
4686     case IDM_LoadNextGame:\r
4687       ReloadGame(1);\r
4688       break;\r
4689 \r
4690     case IDM_LoadPrevGame:\r
4691       ReloadGame(-1);\r
4692       break;\r
4693 \r
4694     case IDM_ReloadGame:\r
4695       ReloadGame(0);\r
4696       break;\r
4697 \r
4698     case IDM_LoadPosition:\r
4699       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4700         Reset(FALSE, TRUE);\r
4701       }\r
4702       number = 1;\r
4703       f = OpenFileDialog(hwnd, "rb", "",\r
4704                          appData.oldSaveStyle ? "pos" : "fen",\r
4705                          POSITION_FILT,\r
4706                          _("Load Position from File"), &number, fileTitle, NULL);\r
4707       if (f != NULL) {\r
4708         LoadPosition(f, number, fileTitle);\r
4709       }\r
4710       break;\r
4711 \r
4712     case IDM_LoadNextPosition:\r
4713       ReloadPosition(1);\r
4714       break;\r
4715 \r
4716     case IDM_LoadPrevPosition:\r
4717       ReloadPosition(-1);\r
4718       break;\r
4719 \r
4720     case IDM_ReloadPosition:\r
4721       ReloadPosition(0);\r
4722       break;\r
4723 \r
4724     case IDM_SaveGame:\r
4725       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4726       f = OpenFileDialog(hwnd, "a", defName,\r
4727                          appData.oldSaveStyle ? "gam" : "pgn",\r
4728                          GAME_FILT,\r
4729                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4730       if (f != NULL) {\r
4731         SaveGame(f, 0, "");\r
4732       }\r
4733       break;\r
4734 \r
4735     case IDM_SavePosition:\r
4736       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4737       f = OpenFileDialog(hwnd, "a", defName,\r
4738                          appData.oldSaveStyle ? "pos" : "fen",\r
4739                          POSITION_FILT,\r
4740                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4741       if (f != NULL) {\r
4742         SavePosition(f, 0, "");\r
4743       }\r
4744       break;\r
4745 \r
4746     case IDM_SaveDiagram:\r
4747       defName = "diagram";\r
4748       f = OpenFileDialog(hwnd, "wb", defName,\r
4749                          "bmp",\r
4750                          DIAGRAM_FILT,\r
4751                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4752       if (f != NULL) {\r
4753         SaveDiagram(f);\r
4754       }\r
4755       break;\r
4756 \r
4757     case IDM_CopyGame:\r
4758       CopyGameToClipboard();\r
4759       break;\r
4760 \r
4761     case IDM_PasteGame:\r
4762       PasteGameFromClipboard();\r
4763       break;\r
4764 \r
4765     case IDM_CopyGameListToClipboard:\r
4766       CopyGameListToClipboard();\r
4767       break;\r
4768 \r
4769     /* [AS] Autodetect FEN or PGN data */\r
4770     case IDM_PasteAny:\r
4771       PasteGameOrFENFromClipboard();\r
4772       break;\r
4773 \r
4774     /* [AS] Move history */\r
4775     case IDM_ShowMoveHistory:\r
4776         if( MoveHistoryIsUp() ) {\r
4777             MoveHistoryPopDown();\r
4778         }\r
4779         else {\r
4780             MoveHistoryPopUp();\r
4781         }\r
4782         break;\r
4783 \r
4784     /* [AS] Eval graph */\r
4785     case IDM_ShowEvalGraph:\r
4786         if( EvalGraphIsUp() ) {\r
4787             EvalGraphPopDown();\r
4788         }\r
4789         else {\r
4790             EvalGraphPopUp();\r
4791             SetFocus(hwndMain);\r
4792         }\r
4793         break;\r
4794 \r
4795     /* [AS] Engine output */\r
4796     case IDM_ShowEngineOutput:\r
4797         if( EngineOutputIsUp() ) {\r
4798             EngineOutputPopDown();\r
4799         }\r
4800         else {\r
4801             EngineOutputPopUp();\r
4802         }\r
4803         break;\r
4804 \r
4805     /* [AS] User adjudication */\r
4806     case IDM_UserAdjudication_White:\r
4807         UserAdjudicationEvent( +1 );\r
4808         break;\r
4809 \r
4810     case IDM_UserAdjudication_Black:\r
4811         UserAdjudicationEvent( -1 );\r
4812         break;\r
4813 \r
4814     case IDM_UserAdjudication_Draw:\r
4815         UserAdjudicationEvent( 0 );\r
4816         break;\r
4817 \r
4818     /* [AS] Game list options dialog */\r
4819     case IDM_GameListOptions:\r
4820       GameListOptions();\r
4821       break;\r
4822 \r
4823     case IDM_NewChat:\r
4824       ChatPopUp(NULL);\r
4825       break;\r
4826 \r
4827     case IDM_CopyPosition:\r
4828       CopyFENToClipboard();\r
4829       break;\r
4830 \r
4831     case IDM_PastePosition:\r
4832       PasteFENFromClipboard();\r
4833       break;\r
4834 \r
4835     case IDM_MailMove:\r
4836       MailMoveEvent();\r
4837       break;\r
4838 \r
4839     case IDM_ReloadCMailMsg:\r
4840       Reset(TRUE, TRUE);\r
4841       ReloadCmailMsgEvent(FALSE);\r
4842       break;\r
4843 \r
4844     case IDM_Minimize:\r
4845       ShowWindow(hwnd, SW_MINIMIZE);\r
4846       break;\r
4847 \r
4848     case IDM_Exit:\r
4849       ExitEvent(0);\r
4850       break;\r
4851 \r
4852     case IDM_MachineWhite:\r
4853       MachineWhiteEvent();\r
4854       /*\r
4855        * refresh the tags dialog only if it's visible\r
4856        */\r
4857       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4858           char *tags;\r
4859           tags = PGNTags(&gameInfo);\r
4860           TagsPopUp(tags, CmailMsg());\r
4861           free(tags);\r
4862       }\r
4863       SAY("computer starts playing white");\r
4864       break;\r
4865 \r
4866     case IDM_MachineBlack:\r
4867       MachineBlackEvent();\r
4868       /*\r
4869        * refresh the tags dialog only if it's visible\r
4870        */\r
4871       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4872           char *tags;\r
4873           tags = PGNTags(&gameInfo);\r
4874           TagsPopUp(tags, CmailMsg());\r
4875           free(tags);\r
4876       }\r
4877       SAY("computer starts playing black");\r
4878       break;\r
4879 \r
4880     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4881       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4882       break;\r
4883 \r
4884     case IDM_TwoMachines:\r
4885       TwoMachinesEvent();\r
4886       /*\r
4887        * refresh the tags dialog only if it's visible\r
4888        */\r
4889       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4890           char *tags;\r
4891           tags = PGNTags(&gameInfo);\r
4892           TagsPopUp(tags, CmailMsg());\r
4893           free(tags);\r
4894       }\r
4895       SAY("computer starts playing both sides");\r
4896       break;\r
4897 \r
4898     case IDM_AnalysisMode:\r
4899       if (!first.analysisSupport) {\r
4900         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4901         DisplayError(buf, 0);\r
4902       } else {\r
4903         SAY("analyzing current position");\r
4904         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4905         if (appData.icsActive) {\r
4906                if (gameMode != IcsObserving) {\r
4907                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4908                        DisplayError(buf, 0);\r
4909                        /* secure check */\r
4910                        if (appData.icsEngineAnalyze) {\r
4911                                if (appData.debugMode) \r
4912                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4913                                ExitAnalyzeMode();\r
4914                                ModeHighlight();\r
4915                                break;\r
4916                        }\r
4917                        break;\r
4918                } else {\r
4919                        /* if enable, user want disable icsEngineAnalyze */\r
4920                        if (appData.icsEngineAnalyze) {\r
4921                                ExitAnalyzeMode();\r
4922                                ModeHighlight();\r
4923                                break;\r
4924                        }\r
4925                        appData.icsEngineAnalyze = TRUE;\r
4926                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4927                }\r
4928         } \r
4929         if (!appData.showThinking) ToggleShowThinking();\r
4930         AnalyzeModeEvent();\r
4931       }\r
4932       break;\r
4933 \r
4934     case IDM_AnalyzeFile:\r
4935       if (!first.analysisSupport) {\r
4936         char buf[MSG_SIZ];\r
4937           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4938         DisplayError(buf, 0);\r
4939       } else {\r
4940         if (!appData.showThinking) ToggleShowThinking();\r
4941         AnalyzeFileEvent();\r
4942 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4943         AnalysisPeriodicEvent(1);\r
4944       }\r
4945       break;\r
4946 \r
4947     case IDM_IcsClient:\r
4948       IcsClientEvent();\r
4949       break;\r
4950 \r
4951     case IDM_EditGame:\r
4952     case IDM_EditGame2:\r
4953       EditGameEvent();\r
4954       SAY("edit game");\r
4955       break;\r
4956 \r
4957     case IDM_EditPosition:\r
4958     case IDM_EditPosition2:\r
4959       EditPositionEvent();\r
4960       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4961       break;\r
4962 \r
4963     case IDM_Training:\r
4964       TrainingEvent();\r
4965       break;\r
4966 \r
4967     case IDM_ShowGameList:\r
4968       ShowGameListProc();\r
4969       break;\r
4970 \r
4971     case IDM_EditProgs1:\r
4972       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4973       break;\r
4974 \r
4975     case IDM_EditProgs2:\r
4976      LoadEnginePopUp(hwndMain);\r
4977 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4978       break;\r
4979 \r
4980     case IDM_EditServers:\r
4981       EditTagsPopUp(icsNames, &icsNames);\r
4982       break;\r
4983 \r
4984     case IDM_EditTags:\r
4985     case IDM_Tags:\r
4986       EditTagsProc();\r
4987       break;\r
4988 \r
4989     case IDM_EditBook:\r
4990       EditBookEvent();\r
4991       break;\r
4992 \r
4993     case IDM_EditComment:\r
4994     case IDM_Comment:\r
4995       if (commentUp && editComment) {\r
4996         CommentPopDown();\r
4997       } else {\r
4998         EditCommentEvent();\r
4999       }\r
5000       break;\r
5001 \r
5002     case IDM_Pause:\r
5003       PauseEvent();\r
5004       break;\r
5005 \r
5006     case IDM_Accept:\r
5007       AcceptEvent();\r
5008       break;\r
5009 \r
5010     case IDM_Decline:\r
5011       DeclineEvent();\r
5012       break;\r
5013 \r
5014     case IDM_Rematch:\r
5015       RematchEvent();\r
5016       break;\r
5017 \r
5018     case IDM_CallFlag:\r
5019       CallFlagEvent();\r
5020       break;\r
5021 \r
5022     case IDM_Draw:\r
5023       DrawEvent();\r
5024       break;\r
5025 \r
5026     case IDM_Adjourn:\r
5027       AdjournEvent();\r
5028       break;\r
5029 \r
5030     case IDM_Abort:\r
5031       AbortEvent();\r
5032       break;\r
5033 \r
5034     case IDM_Resign:\r
5035       ResignEvent();\r
5036       break;\r
5037 \r
5038     case IDM_StopObserving:\r
5039       StopObservingEvent();\r
5040       break;\r
5041 \r
5042     case IDM_StopExamining:\r
5043       StopExaminingEvent();\r
5044       break;\r
5045 \r
5046     case IDM_Upload:\r
5047       UploadGameEvent();\r
5048       break;\r
5049 \r
5050     case IDM_TypeInMove:\r
5051       TypeInEvent('\000');\r
5052       break;\r
5053 \r
5054     case IDM_TypeInName:\r
5055       PopUpNameDialog('\000');\r
5056       break;\r
5057 \r
5058     case IDM_Backward:\r
5059       BackwardEvent();\r
5060       SetFocus(hwndMain);\r
5061       break;\r
5062 \r
5063     JAWS_MENU_ITEMS\r
5064 \r
5065     case IDM_Forward:\r
5066       ForwardEvent();\r
5067       SetFocus(hwndMain);\r
5068       break;\r
5069 \r
5070     case IDM_ToStart:\r
5071       ToStartEvent();\r
5072       SetFocus(hwndMain);\r
5073       break;\r
5074 \r
5075     case IDM_ToEnd:\r
5076       ToEndEvent();\r
5077       SetFocus(hwndMain);\r
5078       break;\r
5079 \r
5080     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5081     case OPT_GameListPrev:\r
5082       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5083       break;\r
5084 \r
5085     case IDM_Revert:\r
5086       RevertEvent(FALSE);\r
5087       break;\r
5088 \r
5089     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5090       RevertEvent(TRUE);\r
5091       break;\r
5092 \r
5093     case IDM_TruncateGame:\r
5094       TruncateGameEvent();\r
5095       break;\r
5096 \r
5097     case IDM_MoveNow:\r
5098       MoveNowEvent();\r
5099       break;\r
5100 \r
5101     case IDM_RetractMove:\r
5102       RetractMoveEvent();\r
5103       break;\r
5104 \r
5105     case IDM_FlipView:\r
5106       flipView = !flipView;\r
5107       DrawPosition(FALSE, NULL);\r
5108       break;\r
5109 \r
5110     case IDM_FlipClock:\r
5111       flipClock = !flipClock;\r
5112       DisplayBothClocks();\r
5113       DisplayLogos();\r
5114       break;\r
5115 \r
5116     case IDM_MuteSounds:\r
5117       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5118       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5119                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5120       break;\r
5121 \r
5122     case IDM_GeneralOptions:\r
5123       GeneralOptionsPopup(hwnd);\r
5124       DrawPosition(TRUE, NULL);\r
5125       break;\r
5126 \r
5127     case IDM_BoardOptions:\r
5128       BoardOptionsPopup(hwnd);\r
5129       break;\r
5130 \r
5131     case IDM_EnginePlayOptions:\r
5132       EnginePlayOptionsPopup(hwnd);\r
5133       break;\r
5134 \r
5135     case IDM_Engine1Options:\r
5136       EngineOptionsPopup(hwnd, &first);\r
5137       break;\r
5138 \r
5139     case IDM_Engine2Options:\r
5140       savedHwnd = hwnd;\r
5141       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5142       EngineOptionsPopup(hwnd, &second);\r
5143       break;\r
5144 \r
5145     case IDM_OptionsUCI:\r
5146       UciOptionsPopup(hwnd);\r
5147       break;\r
5148 \r
5149     case IDM_Tourney:\r
5150       TourneyPopup(hwnd);\r
5151       break;\r
5152 \r
5153     case IDM_IcsOptions:\r
5154       IcsOptionsPopup(hwnd);\r
5155       break;\r
5156 \r
5157     case IDM_Fonts:\r
5158       FontsOptionsPopup(hwnd);\r
5159       break;\r
5160 \r
5161     case IDM_Sounds:\r
5162       SoundOptionsPopup(hwnd);\r
5163       break;\r
5164 \r
5165     case IDM_CommPort:\r
5166       CommPortOptionsPopup(hwnd);\r
5167       break;\r
5168 \r
5169     case IDM_LoadOptions:\r
5170       LoadOptionsPopup(hwnd);\r
5171       break;\r
5172 \r
5173     case IDM_SaveOptions:\r
5174       SaveOptionsPopup(hwnd);\r
5175       break;\r
5176 \r
5177     case IDM_TimeControl:\r
5178       TimeControlOptionsPopup(hwnd);\r
5179       break;\r
5180 \r
5181     case IDM_SaveSettings:\r
5182       SaveSettings(settingsFileName);\r
5183       break;\r
5184 \r
5185     case IDM_SaveSettingsOnExit:\r
5186       saveSettingsOnExit = !saveSettingsOnExit;\r
5187       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5188                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5189                                          MF_CHECKED : MF_UNCHECKED));\r
5190       break;\r
5191 \r
5192     case IDM_Hint:\r
5193       HintEvent();\r
5194       break;\r
5195 \r
5196     case IDM_Book:\r
5197       BookEvent();\r
5198       break;\r
5199 \r
5200     case IDM_AboutGame:\r
5201       AboutGameEvent();\r
5202       break;\r
5203 \r
5204     case IDM_Debug:\r
5205       appData.debugMode = !appData.debugMode;\r
5206       if (appData.debugMode) {\r
5207         char dir[MSG_SIZ];\r
5208         GetCurrentDirectory(MSG_SIZ, dir);\r
5209         SetCurrentDirectory(installDir);\r
5210         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5211         SetCurrentDirectory(dir);\r
5212         setbuf(debugFP, NULL);\r
5213       } else {\r
5214         fclose(debugFP);\r
5215         debugFP = NULL;\r
5216       }\r
5217       break;\r
5218 \r
5219     case IDM_HELPCONTENTS:\r
5220       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5221           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5222           MessageBox (GetFocus(),\r
5223                     _("Unable to activate help"),\r
5224                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5225       }\r
5226       break;\r
5227 \r
5228     case IDM_HELPSEARCH:\r
5229         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5230             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5231         MessageBox (GetFocus(),\r
5232                     _("Unable to activate help"),\r
5233                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5234       }\r
5235       break;\r
5236 \r
5237     case IDM_HELPHELP:\r
5238       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5239         MessageBox (GetFocus(),\r
5240                     _("Unable to activate help"),\r
5241                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5242       }\r
5243       break;\r
5244 \r
5245     case IDM_ABOUT:\r
5246       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5247       DialogBox(hInst, \r
5248         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5249         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5250       FreeProcInstance(lpProc);\r
5251       break;\r
5252 \r
5253     case IDM_DirectCommand1:\r
5254       AskQuestionEvent(_("Direct Command"),\r
5255                        _("Send to chess program:"), "", "1");\r
5256       break;\r
5257     case IDM_DirectCommand2:\r
5258       AskQuestionEvent(_("Direct Command"),\r
5259                        _("Send to second chess program:"), "", "2");\r
5260       break;\r
5261 \r
5262     case EP_WhitePawn:\r
5263       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteKnight:\r
5268       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_WhiteBishop:\r
5273       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_WhiteRook:\r
5278       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_WhiteQueen:\r
5283       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_WhiteFerz:\r
5288       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_WhiteWazir:\r
5293       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_WhiteAlfil:\r
5298       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_WhiteCannon:\r
5303       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_WhiteCardinal:\r
5308       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_WhiteMarshall:\r
5313       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_WhiteKing:\r
5318       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackPawn:\r
5323       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackKnight:\r
5328       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_BlackBishop:\r
5333       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_BlackRook:\r
5338       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_BlackQueen:\r
5343       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_BlackFerz:\r
5348       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_BlackWazir:\r
5353       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_BlackAlfil:\r
5358       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_BlackCannon:\r
5363       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_BlackCardinal:\r
5368       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_BlackMarshall:\r
5373       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case EP_BlackKing:\r
5378       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case EP_EmptySquare:\r
5383       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case EP_ClearBoard:\r
5388       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case EP_White:\r
5393       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case EP_Black:\r
5398       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case EP_Promote:\r
5403       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case EP_Demote:\r
5408       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case DP_Pawn:\r
5413       DropMenuEvent(WhitePawn, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case DP_Knight:\r
5418       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5419       fromX = fromY = -1;\r
5420       break;\r
5421 \r
5422     case DP_Bishop:\r
5423       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5424       fromX = fromY = -1;\r
5425       break;\r
5426 \r
5427     case DP_Rook:\r
5428       DropMenuEvent(WhiteRook, fromX, fromY);\r
5429       fromX = fromY = -1;\r
5430       break;\r
5431 \r
5432     case DP_Queen:\r
5433       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5434       fromX = fromY = -1;\r
5435       break;\r
5436 \r
5437     case IDM_English:\r
5438       barbaric = 0; appData.language = "";\r
5439       TranslateMenus(0);\r
5440       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5441       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5442       lastChecked = wmId;\r
5443       break;\r
5444 \r
5445     default:\r
5446       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5447           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5448           TranslateMenus(0);\r
5449           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5450           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5451           lastChecked = wmId;\r
5452           break;\r
5453       }\r
5454       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5455     }\r
5456     break;\r
5457 \r
5458   case WM_TIMER:\r
5459     switch (wParam) {\r
5460     case CLOCK_TIMER_ID:\r
5461       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5462       clockTimerEvent = 0;\r
5463       DecrementClocks(); /* call into back end */\r
5464       break;\r
5465     case LOAD_GAME_TIMER_ID:\r
5466       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5467       loadGameTimerEvent = 0;\r
5468       AutoPlayGameLoop(); /* call into back end */\r
5469       break;\r
5470     case ANALYSIS_TIMER_ID:\r
5471       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5472                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5473         AnalysisPeriodicEvent(0);\r
5474       } else {\r
5475         KillTimer(hwnd, analysisTimerEvent);\r
5476         analysisTimerEvent = 0;\r
5477       }\r
5478       break;\r
5479     case DELAYED_TIMER_ID:\r
5480       KillTimer(hwnd, delayedTimerEvent);\r
5481       delayedTimerEvent = 0;\r
5482       delayedTimerCallback();\r
5483       break;\r
5484     }\r
5485     break;\r
5486 \r
5487   case WM_USER_Input:\r
5488     InputEvent(hwnd, message, wParam, lParam);\r
5489     break;\r
5490 \r
5491   /* [AS] Also move "attached" child windows */\r
5492   case WM_WINDOWPOSCHANGING:\r
5493 \r
5494     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5495         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5496 \r
5497         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5498             /* Window is moving */\r
5499             RECT rcMain;\r
5500 \r
5501 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5502             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5503             rcMain.right  = wpMain.x + wpMain.width;\r
5504             rcMain.top    = wpMain.y;\r
5505             rcMain.bottom = wpMain.y + wpMain.height;\r
5506             \r
5507             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5508             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5509             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5510             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5511             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5512             wpMain.x = lpwp->x;\r
5513             wpMain.y = lpwp->y;\r
5514         }\r
5515     }\r
5516     break;\r
5517 \r
5518   /* [AS] Snapping */\r
5519   case WM_ENTERSIZEMOVE:\r
5520     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5521     if (hwnd == hwndMain) {\r
5522       doingSizing = TRUE;\r
5523       lastSizing = 0;\r
5524     }\r
5525     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5526     break;\r
5527 \r
5528   case WM_SIZING:\r
5529     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5530     if (hwnd == hwndMain) {\r
5531       lastSizing = wParam;\r
5532     }\r
5533     break;\r
5534 \r
5535   case WM_MOVING:\r
5536     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5537       return OnMoving( &sd, hwnd, wParam, lParam );\r
5538 \r
5539   case WM_EXITSIZEMOVE:\r
5540     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5541     if (hwnd == hwndMain) {\r
5542       RECT client;\r
5543       doingSizing = FALSE;\r
5544       InvalidateRect(hwnd, &boardRect, FALSE);\r
5545       GetClientRect(hwnd, &client);\r
5546       ResizeBoard(client.right, client.bottom, lastSizing);\r
5547       lastSizing = 0;\r
5548       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5549     }\r
5550     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5551     break;\r
5552 \r
5553   case WM_DESTROY: /* message: window being destroyed */\r
5554     PostQuitMessage(0);\r
5555     break;\r
5556 \r
5557   case WM_CLOSE:\r
5558     if (hwnd == hwndMain) {\r
5559       ExitEvent(0);\r
5560     }\r
5561     break;\r
5562 \r
5563   default:      /* Passes it on if unprocessed */\r
5564     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5565   }\r
5566   return 0;\r
5567 }\r
5568 \r
5569 /*---------------------------------------------------------------------------*\\r
5570  *\r
5571  * Misc utility routines\r
5572  *\r
5573 \*---------------------------------------------------------------------------*/\r
5574 \r
5575 /*\r
5576  * Decent random number generator, at least not as bad as Windows\r
5577  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5578  */\r
5579 unsigned int randstate;\r
5580 \r
5581 int\r
5582 myrandom(void)\r
5583 {\r
5584   randstate = randstate * 1664525 + 1013904223;\r
5585   return (int) randstate & 0x7fffffff;\r
5586 }\r
5587 \r
5588 void\r
5589 mysrandom(unsigned int seed)\r
5590 {\r
5591   randstate = seed;\r
5592 }\r
5593 \r
5594 \r
5595 /* \r
5596  * returns TRUE if user selects a different color, FALSE otherwise \r
5597  */\r
5598 \r
5599 BOOL\r
5600 ChangeColor(HWND hwnd, COLORREF *which)\r
5601 {\r
5602   static BOOL firstTime = TRUE;\r
5603   static DWORD customColors[16];\r
5604   CHOOSECOLOR cc;\r
5605   COLORREF newcolor;\r
5606   int i;\r
5607   ColorClass ccl;\r
5608 \r
5609   if (firstTime) {\r
5610     /* Make initial colors in use available as custom colors */\r
5611     /* Should we put the compiled-in defaults here instead? */\r
5612     i = 0;\r
5613     customColors[i++] = lightSquareColor & 0xffffff;\r
5614     customColors[i++] = darkSquareColor & 0xffffff;\r
5615     customColors[i++] = whitePieceColor & 0xffffff;\r
5616     customColors[i++] = blackPieceColor & 0xffffff;\r
5617     customColors[i++] = highlightSquareColor & 0xffffff;\r
5618     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5619 \r
5620     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5621       customColors[i++] = textAttribs[ccl].color;\r
5622     }\r
5623     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5624     firstTime = FALSE;\r
5625   }\r
5626 \r
5627   cc.lStructSize = sizeof(cc);\r
5628   cc.hwndOwner = hwnd;\r
5629   cc.hInstance = NULL;\r
5630   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5631   cc.lpCustColors = (LPDWORD) customColors;\r
5632   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5633 \r
5634   if (!ChooseColor(&cc)) return FALSE;\r
5635 \r
5636   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5637   if (newcolor == *which) return FALSE;\r
5638   *which = newcolor;\r
5639   return TRUE;\r
5640 \r
5641   /*\r
5642   InitDrawingColors();\r
5643   InvalidateRect(hwnd, &boardRect, FALSE);\r
5644   */\r
5645 }\r
5646 \r
5647 BOOLEAN\r
5648 MyLoadSound(MySound *ms)\r
5649 {\r
5650   BOOL ok = FALSE;\r
5651   struct stat st;\r
5652   FILE *f;\r
5653 \r
5654   if (ms->data && ms->flag) free(ms->data);\r
5655   ms->data = NULL;\r
5656 \r
5657   switch (ms->name[0]) {\r
5658   case NULLCHAR:\r
5659     /* Silence */\r
5660     ok = TRUE;\r
5661     break;\r
5662   case '$':\r
5663     /* System sound from Control Panel.  Don't preload here. */\r
5664     ok = TRUE;\r
5665     break;\r
5666   case '!':\r
5667     if (ms->name[1] == NULLCHAR) {\r
5668       /* "!" alone = silence */\r
5669       ok = TRUE;\r
5670     } else {\r
5671       /* Builtin wave resource.  Error if not found. */\r
5672       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5673       if (h == NULL) break;\r
5674       ms->data = (void *)LoadResource(hInst, h);\r
5675       ms->flag = 0; // not maloced, so cannot be freed!\r
5676       if (h == NULL) break;\r
5677       ok = TRUE;\r
5678     }\r
5679     break;\r
5680   default:\r
5681     /* .wav file.  Error if not found. */\r
5682     f = fopen(ms->name, "rb");\r
5683     if (f == NULL) break;\r
5684     if (fstat(fileno(f), &st) < 0) break;\r
5685     ms->data = malloc(st.st_size);\r
5686     ms->flag = 1;\r
5687     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5688     fclose(f);\r
5689     ok = TRUE;\r
5690     break;\r
5691   }\r
5692   if (!ok) {\r
5693     char buf[MSG_SIZ];\r
5694       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5695     DisplayError(buf, GetLastError());\r
5696   }\r
5697   return ok;\r
5698 }\r
5699 \r
5700 BOOLEAN\r
5701 MyPlaySound(MySound *ms)\r
5702 {\r
5703   BOOLEAN ok = FALSE;\r
5704 \r
5705   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5706   switch (ms->name[0]) {\r
5707   case NULLCHAR:\r
5708         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5709     /* Silence */\r
5710     ok = TRUE;\r
5711     break;\r
5712   case '$':\r
5713     /* System sound from Control Panel (deprecated feature).\r
5714        "$" alone or an unset sound name gets default beep (still in use). */\r
5715     if (ms->name[1]) {\r
5716       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5717     }\r
5718     if (!ok) ok = MessageBeep(MB_OK);\r
5719     break; \r
5720   case '!':\r
5721     /* Builtin wave resource, or "!" alone for silence */\r
5722     if (ms->name[1]) {\r
5723       if (ms->data == NULL) return FALSE;\r
5724       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5725     } else {\r
5726       ok = TRUE;\r
5727     }\r
5728     break;\r
5729   default:\r
5730     /* .wav file.  Error if not found. */\r
5731     if (ms->data == NULL) return FALSE;\r
5732     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5733     break;\r
5734   }\r
5735   /* Don't print an error: this can happen innocently if the sound driver\r
5736      is busy; for instance, if another instance of WinBoard is playing\r
5737      a sound at about the same time. */\r
5738   return ok;\r
5739 }\r
5740 \r
5741 \r
5742 LRESULT CALLBACK\r
5743 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5744 {\r
5745   BOOL ok;\r
5746   OPENFILENAME *ofn;\r
5747   static UINT *number; /* gross that this is static */\r
5748 \r
5749   switch (message) {\r
5750   case WM_INITDIALOG: /* message: initialize dialog box */\r
5751     /* Center the dialog over the application window */\r
5752     ofn = (OPENFILENAME *) lParam;\r
5753     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5754       number = (UINT *) ofn->lCustData;\r
5755       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5756     } else {\r
5757       number = NULL;\r
5758     }\r
5759     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5760     Translate(hDlg, 1536);\r
5761     return FALSE;  /* Allow for further processing */\r
5762 \r
5763   case WM_COMMAND:\r
5764     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5765       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5766     }\r
5767     return FALSE;  /* Allow for further processing */\r
5768   }\r
5769   return FALSE;\r
5770 }\r
5771 \r
5772 UINT APIENTRY\r
5773 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5774 {\r
5775   static UINT *number;\r
5776   OPENFILENAME *ofname;\r
5777   OFNOTIFY *ofnot;\r
5778   switch (uiMsg) {\r
5779   case WM_INITDIALOG:\r
5780     Translate(hdlg, DLG_IndexNumber);\r
5781     ofname = (OPENFILENAME *)lParam;\r
5782     number = (UINT *)(ofname->lCustData);\r
5783     break;\r
5784   case WM_NOTIFY:\r
5785     ofnot = (OFNOTIFY *)lParam;\r
5786     if (ofnot->hdr.code == CDN_FILEOK) {\r
5787       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5788     }\r
5789     break;\r
5790   }\r
5791   return 0;\r
5792 }\r
5793 \r
5794 \r
5795 FILE *\r
5796 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5797                char *nameFilt, char *dlgTitle, UINT *number,\r
5798                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5799 {\r
5800   OPENFILENAME openFileName;\r
5801   char buf1[MSG_SIZ];\r
5802   FILE *f;\r
5803 \r
5804   if (fileName == NULL) fileName = buf1;\r
5805   if (defName == NULL) {\r
5806     safeStrCpy(fileName, "*.", 3 );\r
5807     strcat(fileName, defExt);\r
5808   } else {\r
5809     safeStrCpy(fileName, defName, MSG_SIZ );\r
5810   }\r
5811     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5812   if (number) *number = 0;\r
5813 \r
5814   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5815   openFileName.hwndOwner         = hwnd;\r
5816   openFileName.hInstance         = (HANDLE) hInst;\r
5817   openFileName.lpstrFilter       = nameFilt;\r
5818   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5819   openFileName.nMaxCustFilter    = 0L;\r
5820   openFileName.nFilterIndex      = 1L;\r
5821   openFileName.lpstrFile         = fileName;\r
5822   openFileName.nMaxFile          = MSG_SIZ;\r
5823   openFileName.lpstrFileTitle    = fileTitle;\r
5824   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5825   openFileName.lpstrInitialDir   = NULL;\r
5826   openFileName.lpstrTitle        = dlgTitle;\r
5827   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5828     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5829     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5830     | (oldDialog ? 0 : OFN_EXPLORER);\r
5831   openFileName.nFileOffset       = 0;\r
5832   openFileName.nFileExtension    = 0;\r
5833   openFileName.lpstrDefExt       = defExt;\r
5834   openFileName.lCustData         = (LONG) number;\r
5835   openFileName.lpfnHook          = oldDialog ?\r
5836     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5837   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5838 \r
5839   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5840                         GetOpenFileName(&openFileName)) {\r
5841     /* open the file */\r
5842     f = fopen(openFileName.lpstrFile, write);\r
5843     if (f == NULL) {\r
5844       MessageBox(hwnd, _("File open failed"), NULL,\r
5845                  MB_OK|MB_ICONEXCLAMATION);\r
5846       return NULL;\r
5847     }\r
5848   } else {\r
5849     int err = CommDlgExtendedError();\r
5850     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5851     return FALSE;\r
5852   }\r
5853   return f;\r
5854 }\r
5855 \r
5856 \r
5857 \r
5858 VOID APIENTRY\r
5859 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5860 {\r
5861   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5862 \r
5863   /*\r
5864    * Get the first pop-up menu in the menu template. This is the\r
5865    * menu that TrackPopupMenu displays.\r
5866    */\r
5867   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5868   TranslateOneMenu(10, hmenuTrackPopup);\r
5869 \r
5870   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5871 \r
5872   /*\r
5873    * TrackPopup uses screen coordinates, so convert the\r
5874    * coordinates of the mouse click to screen coordinates.\r
5875    */\r
5876   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5877 \r
5878   /* Draw and track the floating pop-up menu. */\r
5879   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5880                  pt.x, pt.y, 0, hwnd, NULL);\r
5881 \r
5882   /* Destroy the menu.*/\r
5883   DestroyMenu(hmenu);\r
5884 }\r
5885    \r
5886 typedef struct {\r
5887   HWND hDlg, hText;\r
5888   int sizeX, sizeY, newSizeX, newSizeY;\r
5889   HDWP hdwp;\r
5890 } ResizeEditPlusButtonsClosure;\r
5891 \r
5892 BOOL CALLBACK\r
5893 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5894 {\r
5895   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5896   RECT rect;\r
5897   POINT pt;\r
5898 \r
5899   if (hChild == cl->hText) return TRUE;\r
5900   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5901   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5902   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5903   ScreenToClient(cl->hDlg, &pt);\r
5904   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5905     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5906   return TRUE;\r
5907 }\r
5908 \r
5909 /* Resize a dialog that has a (rich) edit field filling most of\r
5910    the top, with a row of buttons below */\r
5911 VOID\r
5912 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5913 {\r
5914   RECT rectText;\r
5915   int newTextHeight, newTextWidth;\r
5916   ResizeEditPlusButtonsClosure cl;\r
5917   \r
5918   /*if (IsIconic(hDlg)) return;*/\r
5919   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5920   \r
5921   cl.hdwp = BeginDeferWindowPos(8);\r
5922 \r
5923   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5924   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5925   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5926   if (newTextHeight < 0) {\r
5927     newSizeY += -newTextHeight;\r
5928     newTextHeight = 0;\r
5929   }\r
5930   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5931     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5932 \r
5933   cl.hDlg = hDlg;\r
5934   cl.hText = hText;\r
5935   cl.sizeX = sizeX;\r
5936   cl.sizeY = sizeY;\r
5937   cl.newSizeX = newSizeX;\r
5938   cl.newSizeY = newSizeY;\r
5939   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5940 \r
5941   EndDeferWindowPos(cl.hdwp);\r
5942 }\r
5943 \r
5944 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5945 {\r
5946     RECT    rChild, rParent;\r
5947     int     wChild, hChild, wParent, hParent;\r
5948     int     wScreen, hScreen, xNew, yNew;\r
5949     HDC     hdc;\r
5950 \r
5951     /* Get the Height and Width of the child window */\r
5952     GetWindowRect (hwndChild, &rChild);\r
5953     wChild = rChild.right - rChild.left;\r
5954     hChild = rChild.bottom - rChild.top;\r
5955 \r
5956     /* Get the Height and Width of the parent window */\r
5957     GetWindowRect (hwndParent, &rParent);\r
5958     wParent = rParent.right - rParent.left;\r
5959     hParent = rParent.bottom - rParent.top;\r
5960 \r
5961     /* Get the display limits */\r
5962     hdc = GetDC (hwndChild);\r
5963     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5964     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5965     ReleaseDC(hwndChild, hdc);\r
5966 \r
5967     /* Calculate new X position, then adjust for screen */\r
5968     xNew = rParent.left + ((wParent - wChild) /2);\r
5969     if (xNew < 0) {\r
5970         xNew = 0;\r
5971     } else if ((xNew+wChild) > wScreen) {\r
5972         xNew = wScreen - wChild;\r
5973     }\r
5974 \r
5975     /* Calculate new Y position, then adjust for screen */\r
5976     if( mode == 0 ) {\r
5977         yNew = rParent.top  + ((hParent - hChild) /2);\r
5978     }\r
5979     else {\r
5980         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5981     }\r
5982 \r
5983     if (yNew < 0) {\r
5984         yNew = 0;\r
5985     } else if ((yNew+hChild) > hScreen) {\r
5986         yNew = hScreen - hChild;\r
5987     }\r
5988 \r
5989     /* Set it, and return */\r
5990     return SetWindowPos (hwndChild, NULL,\r
5991                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5992 }\r
5993 \r
5994 /* Center one window over another */\r
5995 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5996 {\r
5997     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5998 }\r
5999 \r
6000 /*---------------------------------------------------------------------------*\\r
6001  *\r
6002  * Startup Dialog functions\r
6003  *\r
6004 \*---------------------------------------------------------------------------*/\r
6005 void\r
6006 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6007 {\r
6008   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6009 \r
6010   while (*cd != NULL) {\r
6011     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6012     cd++;\r
6013   }\r
6014 }\r
6015 \r
6016 void\r
6017 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6018 {\r
6019   char buf1[MAX_ARG_LEN];\r
6020   int len;\r
6021 \r
6022   if (str[0] == '@') {\r
6023     FILE* f = fopen(str + 1, "r");\r
6024     if (f == NULL) {\r
6025       DisplayFatalError(str + 1, errno, 2);\r
6026       return;\r
6027     }\r
6028     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6029     fclose(f);\r
6030     buf1[len] = NULLCHAR;\r
6031     str = buf1;\r
6032   }\r
6033 \r
6034   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6035 \r
6036   for (;;) {\r
6037     char buf[MSG_SIZ];\r
6038     char *end = strchr(str, '\n');\r
6039     if (end == NULL) return;\r
6040     memcpy(buf, str, end - str);\r
6041     buf[end - str] = NULLCHAR;\r
6042     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6043     str = end + 1;\r
6044   }\r
6045 }\r
6046 \r
6047 void\r
6048 SetStartupDialogEnables(HWND hDlg)\r
6049 {\r
6050   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6051     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6052     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6053   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6054     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6055   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6056     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6057   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6058     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6059   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6060     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6061     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6062     IsDlgButtonChecked(hDlg, OPT_View));\r
6063 }\r
6064 \r
6065 char *\r
6066 QuoteForFilename(char *filename)\r
6067 {\r
6068   int dquote, space;\r
6069   dquote = strchr(filename, '"') != NULL;\r
6070   space = strchr(filename, ' ') != NULL;\r
6071   if (dquote || space) {\r
6072     if (dquote) {\r
6073       return "'";\r
6074     } else {\r
6075       return "\"";\r
6076     }\r
6077   } else {\r
6078     return "";\r
6079   }\r
6080 }\r
6081 \r
6082 VOID\r
6083 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6084 {\r
6085   char buf[MSG_SIZ];\r
6086   char *q;\r
6087 \r
6088   InitComboStringsFromOption(hwndCombo, nthnames);\r
6089   q = QuoteForFilename(nthcp);\r
6090     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6091   if (*nthdir != NULLCHAR) {\r
6092     q = QuoteForFilename(nthdir);\r
6093       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6094   }\r
6095   if (*nthcp == NULLCHAR) {\r
6096     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6097   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6098     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6099     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6100   }\r
6101 }\r
6102 \r
6103 LRESULT CALLBACK\r
6104 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6105 {\r
6106   char buf[MSG_SIZ];\r
6107   HANDLE hwndCombo;\r
6108   char *p;\r
6109 \r
6110   switch (message) {\r
6111   case WM_INITDIALOG:\r
6112     /* Center the dialog */\r
6113     CenterWindow (hDlg, GetDesktopWindow());\r
6114     Translate(hDlg, DLG_Startup);\r
6115     /* Initialize the dialog items */\r
6116     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6117                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6118                   firstChessProgramNames);\r
6119     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6120                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6121                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6122     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6123     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6124       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6125     if (*appData.icsHelper != NULLCHAR) {\r
6126       char *q = QuoteForFilename(appData.icsHelper);\r
6127       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6128     }\r
6129     if (*appData.icsHost == NULLCHAR) {\r
6130       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6131       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6132     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6133       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6134       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6135     }\r
6136 \r
6137     if (appData.icsActive) {\r
6138       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6139     }\r
6140     else if (appData.noChessProgram) {\r
6141       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6142     }\r
6143     else {\r
6144       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6145     }\r
6146 \r
6147     SetStartupDialogEnables(hDlg);\r
6148     return TRUE;\r
6149 \r
6150   case WM_COMMAND:\r
6151     switch (LOWORD(wParam)) {\r
6152     case IDOK:\r
6153       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6154         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6155         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6156         p = buf;\r
6157         ParseArgs(StringGet, &p);\r
6158         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6159         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6160         p = buf;
6161         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6162         ParseArgs(StringGet, &p);\r
6163         SwapEngines(singleList); // ... and then make it 'second'\r
6164         appData.noChessProgram = FALSE;\r
6165         appData.icsActive = FALSE;\r
6166       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6167         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6168         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6169         p = buf;\r
6170         ParseArgs(StringGet, &p);\r
6171         if (appData.zippyPlay) {\r
6172           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6173           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6174           p = buf;\r
6175           ParseArgs(StringGet, &p);\r
6176         }\r
6177       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6178         appData.noChessProgram = TRUE;\r
6179         appData.icsActive = FALSE;\r
6180       } else {\r
6181         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6182                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6183         return TRUE;\r
6184       }\r
6185       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6186         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6187         p = buf;\r
6188         ParseArgs(StringGet, &p);\r
6189       }\r
6190       EndDialog(hDlg, TRUE);\r
6191       return TRUE;\r
6192 \r
6193     case IDCANCEL:\r
6194       ExitEvent(0);\r
6195       return TRUE;\r
6196 \r
6197     case IDM_HELPCONTENTS:\r
6198       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6199         MessageBox (GetFocus(),\r
6200                     _("Unable to activate help"),\r
6201                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6202       }\r
6203       break;\r
6204 \r
6205     default:\r
6206       SetStartupDialogEnables(hDlg);\r
6207       break;\r
6208     }\r
6209     break;\r
6210   }\r
6211   return FALSE;\r
6212 }\r
6213 \r
6214 /*---------------------------------------------------------------------------*\\r
6215  *\r
6216  * About box dialog functions\r
6217  *\r
6218 \*---------------------------------------------------------------------------*/\r
6219 \r
6220 /* Process messages for "About" dialog box */\r
6221 LRESULT CALLBACK\r
6222 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6223 {\r
6224   switch (message) {\r
6225   case WM_INITDIALOG: /* message: initialize dialog box */\r
6226     /* Center the dialog over the application window */\r
6227     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6228     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6229     Translate(hDlg, ABOUTBOX);\r
6230     JAWS_COPYRIGHT\r
6231     return (TRUE);\r
6232 \r
6233   case WM_COMMAND: /* message: received a command */\r
6234     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6235         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6236       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6237       return (TRUE);\r
6238     }\r
6239     break;\r
6240   }\r
6241   return (FALSE);\r
6242 }\r
6243 \r
6244 /*---------------------------------------------------------------------------*\\r
6245  *\r
6246  * Comment Dialog functions\r
6247  *\r
6248 \*---------------------------------------------------------------------------*/\r
6249 \r
6250 LRESULT CALLBACK\r
6251 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6252 {\r
6253   static HANDLE hwndText = NULL;\r
6254   int len, newSizeX, newSizeY, flags;\r
6255   static int sizeX, sizeY;\r
6256   char *str;\r
6257   RECT rect;\r
6258   MINMAXINFO *mmi;\r
6259 \r
6260   switch (message) {\r
6261   case WM_INITDIALOG: /* message: initialize dialog box */\r
6262     /* Initialize the dialog items */\r
6263     Translate(hDlg, DLG_EditComment);\r
6264     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6265     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6266     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6267     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6268     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6269     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6270     SetWindowText(hDlg, commentTitle);\r
6271     if (editComment) {\r
6272       SetFocus(hwndText);\r
6273     } else {\r
6274       SetFocus(GetDlgItem(hDlg, IDOK));\r
6275     }\r
6276     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6277                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6278                 MAKELPARAM(FALSE, 0));\r
6279     /* Size and position the dialog */\r
6280     if (!commentDialog) {\r
6281       commentDialog = hDlg;\r
6282       flags = SWP_NOZORDER;\r
6283       GetClientRect(hDlg, &rect);\r
6284       sizeX = rect.right;\r
6285       sizeY = rect.bottom;\r
6286       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6287           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6288         WINDOWPLACEMENT wp;\r
6289         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6290         wp.length = sizeof(WINDOWPLACEMENT);\r
6291         wp.flags = 0;\r
6292         wp.showCmd = SW_SHOW;\r
6293         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6294         wp.rcNormalPosition.left = wpComment.x;\r
6295         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6296         wp.rcNormalPosition.top = wpComment.y;\r
6297         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6298         SetWindowPlacement(hDlg, &wp);\r
6299 \r
6300         GetClientRect(hDlg, &rect);\r
6301         newSizeX = rect.right;\r
6302         newSizeY = rect.bottom;\r
6303         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6304                               newSizeX, newSizeY);\r
6305         sizeX = newSizeX;\r
6306         sizeY = newSizeY;\r
6307       }\r
6308     }\r
6309     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6310     return FALSE;\r
6311 \r
6312   case WM_COMMAND: /* message: received a command */\r
6313     switch (LOWORD(wParam)) {\r
6314     case IDOK:\r
6315       if (editComment) {\r
6316         char *p, *q;\r
6317         /* Read changed options from the dialog box */\r
6318         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6319         len = GetWindowTextLength(hwndText);\r
6320         str = (char *) malloc(len + 1);\r
6321         GetWindowText(hwndText, str, len + 1);\r
6322         p = q = str;\r
6323         while (*q) {\r
6324           if (*q == '\r')\r
6325             q++;\r
6326           else\r
6327             *p++ = *q++;\r
6328         }\r
6329         *p = NULLCHAR;\r
6330         ReplaceComment(commentIndex, str);\r
6331         free(str);\r
6332       }\r
6333       CommentPopDown();\r
6334       return TRUE;\r
6335 \r
6336     case IDCANCEL:\r
6337     case OPT_CancelComment:\r
6338       CommentPopDown();\r
6339       return TRUE;\r
6340 \r
6341     case OPT_ClearComment:\r
6342       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6343       break;\r
6344 \r
6345     case OPT_EditComment:\r
6346       EditCommentEvent();\r
6347       return TRUE;\r
6348 \r
6349     default:\r
6350       break;\r
6351     }\r
6352     break;\r
6353 \r
6354   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6355         if( wParam == OPT_CommentText ) {\r
6356             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6357 \r
6358             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6359                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6360                 POINTL pt;\r
6361                 LRESULT index;\r
6362 \r
6363                 pt.x = LOWORD( lpMF->lParam );\r
6364                 pt.y = HIWORD( lpMF->lParam );\r
6365 \r
6366                 if(lpMF->msg == WM_CHAR) {\r
6367                         CHARRANGE sel;\r
6368                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6369                         index = sel.cpMin;\r
6370                 } else\r
6371                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6372 \r
6373                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6374                 len = GetWindowTextLength(hwndText);\r
6375                 str = (char *) malloc(len + 1);\r
6376                 GetWindowText(hwndText, str, len + 1);\r
6377                 ReplaceComment(commentIndex, str);\r
6378                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6379                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6380                 free(str);\r
6381 \r
6382                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6383                 lpMF->msg = WM_USER;\r
6384 \r
6385                 return TRUE;\r
6386             }\r
6387         }\r
6388         break;\r
6389 \r
6390   case WM_SIZE:\r
6391     newSizeX = LOWORD(lParam);\r
6392     newSizeY = HIWORD(lParam);\r
6393     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6394     sizeX = newSizeX;\r
6395     sizeY = newSizeY;\r
6396     break;\r
6397 \r
6398   case WM_GETMINMAXINFO:\r
6399     /* Prevent resizing window too small */\r
6400     mmi = (MINMAXINFO *) lParam;\r
6401     mmi->ptMinTrackSize.x = 100;\r
6402     mmi->ptMinTrackSize.y = 100;\r
6403     break;\r
6404   }\r
6405   return FALSE;\r
6406 }\r
6407 \r
6408 VOID\r
6409 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6410 {\r
6411   FARPROC lpProc;\r
6412   char *p, *q;\r
6413 \r
6414   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6415 \r
6416   if (str == NULL) str = "";\r
6417   p = (char *) malloc(2 * strlen(str) + 2);\r
6418   q = p;\r
6419   while (*str) {\r
6420     if (*str == '\n') *q++ = '\r';\r
6421     *q++ = *str++;\r
6422   }\r
6423   *q = NULLCHAR;\r
6424   if (commentText != NULL) free(commentText);\r
6425 \r
6426   commentIndex = index;\r
6427   commentTitle = title;\r
6428   commentText = p;\r
6429   editComment = edit;\r
6430 \r
6431   if (commentDialog) {\r
6432     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6433     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6434   } else {\r
6435     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6436     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6437                  hwndMain, (DLGPROC)lpProc);\r
6438     FreeProcInstance(lpProc);\r
6439   }\r
6440   commentUp = TRUE;\r
6441 }\r
6442 \r
6443 \r
6444 /*---------------------------------------------------------------------------*\\r
6445  *\r
6446  * Type-in move dialog functions\r
6447  * \r
6448 \*---------------------------------------------------------------------------*/\r
6449 \r
6450 LRESULT CALLBACK\r
6451 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6452 {\r
6453   char move[MSG_SIZ];\r
6454   HWND hInput;\r
6455 \r
6456   switch (message) {\r
6457   case WM_INITDIALOG:\r
6458     move[0] = (char) lParam;\r
6459     move[1] = NULLCHAR;\r
6460     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6461     Translate(hDlg, DLG_TypeInMove);\r
6462     hInput = GetDlgItem(hDlg, OPT_Move);\r
6463     SetWindowText(hInput, move);\r
6464     SetFocus(hInput);\r
6465     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6466     return FALSE;\r
6467 \r
6468   case WM_COMMAND:\r
6469     switch (LOWORD(wParam)) {\r
6470     case IDOK:
6471 \r
6472       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6473       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6474       TypeInDoneEvent(move);\r
6475       EndDialog(hDlg, TRUE);\r
6476       return TRUE;\r
6477     case IDCANCEL:\r
6478       EndDialog(hDlg, FALSE);\r
6479       return TRUE;\r
6480     default:\r
6481       break;\r
6482     }\r
6483     break;\r
6484   }\r
6485   return FALSE;\r
6486 }\r
6487 \r
6488 VOID\r
6489 PopUpMoveDialog(char firstchar)\r
6490 {\r
6491     FARPROC lpProc;\r
6492 \r
6493       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6494       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6495         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6496       FreeProcInstance(lpProc);\r
6497 }\r
6498 \r
6499 /*---------------------------------------------------------------------------*\\r
6500  *\r
6501  * Type-in name dialog functions\r
6502  * \r
6503 \*---------------------------------------------------------------------------*/\r
6504 \r
6505 LRESULT CALLBACK\r
6506 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6507 {\r
6508   char move[MSG_SIZ];\r
6509   HWND hInput;\r
6510 \r
6511   switch (message) {\r
6512   case WM_INITDIALOG:\r
6513     move[0] = (char) lParam;\r
6514     move[1] = NULLCHAR;\r
6515     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6516     Translate(hDlg, DLG_TypeInName);\r
6517     hInput = GetDlgItem(hDlg, OPT_Name);\r
6518     SetWindowText(hInput, move);\r
6519     SetFocus(hInput);\r
6520     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6521     return FALSE;\r
6522 \r
6523   case WM_COMMAND:\r
6524     switch (LOWORD(wParam)) {\r
6525     case IDOK:\r
6526       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6527       appData.userName = strdup(move);\r
6528       SetUserLogo();\r
6529       SetGameInfo();\r
6530       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6531         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6532         DisplayTitle(move);\r
6533       }\r
6534 \r
6535 \r
6536       EndDialog(hDlg, TRUE);\r
6537       return TRUE;\r
6538     case IDCANCEL:\r
6539       EndDialog(hDlg, FALSE);\r
6540       return TRUE;\r
6541     default:\r
6542       break;\r
6543     }\r
6544     break;\r
6545   }\r
6546   return FALSE;\r
6547 }\r
6548 \r
6549 VOID\r
6550 PopUpNameDialog(char firstchar)\r
6551 {\r
6552     FARPROC lpProc;\r
6553     \r
6554       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6555       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6556         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6557       FreeProcInstance(lpProc);\r
6558 }\r
6559 \r
6560 /*---------------------------------------------------------------------------*\\r
6561  *\r
6562  *  Error dialogs\r
6563  * \r
6564 \*---------------------------------------------------------------------------*/\r
6565 \r
6566 /* Nonmodal error box */\r
6567 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6568                              WPARAM wParam, LPARAM lParam);\r
6569 \r
6570 VOID\r
6571 ErrorPopUp(char *title, char *content)\r
6572 {\r
6573   FARPROC lpProc;\r
6574   char *p, *q;\r
6575   BOOLEAN modal = hwndMain == NULL;\r
6576 \r
6577   p = content;\r
6578   q = errorMessage;\r
6579   while (*p) {\r
6580     if (*p == '\n') {\r
6581       if (modal) {\r
6582         *q++ = ' ';\r
6583         p++;\r
6584       } else {\r
6585         *q++ = '\r';\r
6586         *q++ = *p++;\r
6587       }\r
6588     } else {\r
6589       *q++ = *p++;\r
6590     }\r
6591   }\r
6592   *q = NULLCHAR;\r
6593   strncpy(errorTitle, title, sizeof(errorTitle));\r
6594   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6595   \r
6596   if (modal) {\r
6597     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6598   } else {\r
6599     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6600     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6601                  hwndMain, (DLGPROC)lpProc);\r
6602     FreeProcInstance(lpProc);\r
6603   }\r
6604 }\r
6605 \r
6606 VOID\r
6607 ErrorPopDown()\r
6608 {\r
6609   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6610   if (errorDialog == NULL) return;\r
6611   DestroyWindow(errorDialog);\r
6612   errorDialog = NULL;\r
6613   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6614 }\r
6615 \r
6616 LRESULT CALLBACK\r
6617 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6618 {\r
6619   HANDLE hwndText;\r
6620   RECT rChild;\r
6621 \r
6622   switch (message) {\r
6623   case WM_INITDIALOG:\r
6624     GetWindowRect(hDlg, &rChild);\r
6625 \r
6626     /*\r
6627     SetWindowPos(hDlg, NULL, rChild.left,\r
6628       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6629       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6630     */\r
6631 \r
6632     /* \r
6633         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6634         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6635         and it doesn't work when you resize the dialog.\r
6636         For now, just give it a default position.\r
6637     */\r
6638     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6639     Translate(hDlg, DLG_Error);\r
6640 \r
6641     errorDialog = hDlg;\r
6642     SetWindowText(hDlg, errorTitle);\r
6643     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6644     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6645     return FALSE;\r
6646 \r
6647   case WM_COMMAND:\r
6648     switch (LOWORD(wParam)) {\r
6649     case IDOK:\r
6650     case IDCANCEL:\r
6651       if (errorDialog == hDlg) errorDialog = NULL;\r
6652       DestroyWindow(hDlg);\r
6653       return TRUE;\r
6654 \r
6655     default:\r
6656       break;\r
6657     }\r
6658     break;\r
6659   }\r
6660   return FALSE;\r
6661 }\r
6662 \r
6663 #ifdef GOTHIC\r
6664 HWND gothicDialog = NULL;\r
6665 \r
6666 LRESULT CALLBACK\r
6667 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6668 {\r
6669   HANDLE hwndText;\r
6670   RECT rChild;\r
6671   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6672 \r
6673   switch (message) {\r
6674   case WM_INITDIALOG:\r
6675     GetWindowRect(hDlg, &rChild);\r
6676 \r
6677     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6678                                                              SWP_NOZORDER);\r
6679 \r
6680     /* \r
6681         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6682         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6683         and it doesn't work when you resize the dialog.\r
6684         For now, just give it a default position.\r
6685     */\r
6686     gothicDialog = hDlg;\r
6687     SetWindowText(hDlg, errorTitle);\r
6688     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6689     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6690     return FALSE;\r
6691 \r
6692   case WM_COMMAND:\r
6693     switch (LOWORD(wParam)) {\r
6694     case IDOK:\r
6695     case IDCANCEL:\r
6696       if (errorDialog == hDlg) errorDialog = NULL;\r
6697       DestroyWindow(hDlg);\r
6698       return TRUE;\r
6699 \r
6700     default:\r
6701       break;\r
6702     }\r
6703     break;\r
6704   }\r
6705   return FALSE;\r
6706 }\r
6707 \r
6708 VOID\r
6709 GothicPopUp(char *title, VariantClass variant)\r
6710 {\r
6711   FARPROC lpProc;\r
6712   static char *lastTitle;\r
6713 \r
6714   strncpy(errorTitle, title, sizeof(errorTitle));\r
6715   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6716 \r
6717   if(lastTitle != title && gothicDialog != NULL) {\r
6718     DestroyWindow(gothicDialog);\r
6719     gothicDialog = NULL;\r
6720   }\r
6721   if(variant != VariantNormal && gothicDialog == NULL) {\r
6722     title = lastTitle;\r
6723     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6724     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6725                  hwndMain, (DLGPROC)lpProc);\r
6726     FreeProcInstance(lpProc);\r
6727   }\r
6728 }\r
6729 #endif\r
6730 \r
6731 /*---------------------------------------------------------------------------*\\r
6732  *\r
6733  *  Ics Interaction console functions\r
6734  *\r
6735 \*---------------------------------------------------------------------------*/\r
6736 \r
6737 #define HISTORY_SIZE 64\r
6738 static char *history[HISTORY_SIZE];\r
6739 int histIn = 0, histP = 0;\r
6740 \r
6741 VOID\r
6742 SaveInHistory(char *cmd)\r
6743 {\r
6744   if (history[histIn] != NULL) {\r
6745     free(history[histIn]);\r
6746     history[histIn] = NULL;\r
6747   }\r
6748   if (*cmd == NULLCHAR) return;\r
6749   history[histIn] = StrSave(cmd);\r
6750   histIn = (histIn + 1) % HISTORY_SIZE;\r
6751   if (history[histIn] != NULL) {\r
6752     free(history[histIn]);\r
6753     history[histIn] = NULL;\r
6754   }\r
6755   histP = histIn;\r
6756 }\r
6757 \r
6758 char *\r
6759 PrevInHistory(char *cmd)\r
6760 {\r
6761   int newhp;\r
6762   if (histP == histIn) {\r
6763     if (history[histIn] != NULL) free(history[histIn]);\r
6764     history[histIn] = StrSave(cmd);\r
6765   }\r
6766   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6767   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6768   histP = newhp;\r
6769   return history[histP];\r
6770 }\r
6771 \r
6772 char *\r
6773 NextInHistory()\r
6774 {\r
6775   if (histP == histIn) return NULL;\r
6776   histP = (histP + 1) % HISTORY_SIZE;\r
6777   return history[histP];   \r
6778 }\r
6779 \r
6780 HMENU\r
6781 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6782 {\r
6783   HMENU hmenu, h;\r
6784   int i = 0;\r
6785   hmenu = LoadMenu(hInst, "TextMenu");\r
6786   h = GetSubMenu(hmenu, 0);\r
6787   while (e->item) {\r
6788     if (strcmp(e->item, "-") == 0) {\r
6789       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6790     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6791       int flags = MF_STRING, j = 0;\r
6792       if (e->item[0] == '|') {\r
6793         flags |= MF_MENUBARBREAK;\r
6794         j++;\r
6795       }\r
6796       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6797       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6798     }\r
6799     e++;\r
6800     i++;\r
6801   } \r
6802   return hmenu;\r
6803 }\r
6804 \r
6805 WNDPROC consoleTextWindowProc;\r
6806 \r
6807 void\r
6808 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6809 {\r
6810   char buf[MSG_SIZ], name[MSG_SIZ];\r
6811   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6812   CHARRANGE sel;\r
6813 \r
6814   if (!getname) {\r
6815     SetWindowText(hInput, command);\r
6816     if (immediate) {\r
6817       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6818     } else {\r
6819       sel.cpMin = 999999;\r
6820       sel.cpMax = 999999;\r
6821       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6822       SetFocus(hInput);\r
6823     }\r
6824     return;\r
6825   }    \r
6826   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6827   if (sel.cpMin == sel.cpMax) {\r
6828     /* Expand to surrounding word */\r
6829     TEXTRANGE tr;\r
6830     do {\r
6831       tr.chrg.cpMax = sel.cpMin;\r
6832       tr.chrg.cpMin = --sel.cpMin;\r
6833       if (sel.cpMin < 0) break;\r
6834       tr.lpstrText = name;\r
6835       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6836     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6837     sel.cpMin++;\r
6838 \r
6839     do {\r
6840       tr.chrg.cpMin = sel.cpMax;\r
6841       tr.chrg.cpMax = ++sel.cpMax;\r
6842       tr.lpstrText = name;\r
6843       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6844     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6845     sel.cpMax--;\r
6846 \r
6847     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6848       MessageBeep(MB_ICONEXCLAMATION);\r
6849       return;\r
6850     }\r
6851     tr.chrg = sel;\r
6852     tr.lpstrText = name;\r
6853     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6854   } else {\r
6855     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6856       MessageBeep(MB_ICONEXCLAMATION);\r
6857       return;\r
6858     }\r
6859     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6860   }\r
6861   if (immediate) {\r
6862     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6863     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6864     SetWindowText(hInput, buf);\r
6865     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6866   } else {\r
6867     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6868       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6869     SetWindowText(hInput, buf);\r
6870     sel.cpMin = 999999;\r
6871     sel.cpMax = 999999;\r
6872     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6873     SetFocus(hInput);\r
6874   }\r
6875 }\r
6876 \r
6877 LRESULT CALLBACK \r
6878 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6879 {\r
6880   HWND hInput;\r
6881   CHARRANGE sel;\r
6882 \r
6883   switch (message) {\r
6884   case WM_KEYDOWN:\r
6885     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6886     if(wParam=='R') return 0;\r
6887     switch (wParam) {\r
6888     case VK_PRIOR:\r
6889       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6890       return 0;\r
6891     case VK_NEXT:\r
6892       sel.cpMin = 999999;\r
6893       sel.cpMax = 999999;\r
6894       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6895       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6896       return 0;\r
6897     }\r
6898     break;\r
6899   case WM_CHAR:\r
6900    if(wParam != '\022') {\r
6901     if (wParam == '\t') {\r
6902       if (GetKeyState(VK_SHIFT) < 0) {\r
6903         /* shifted */\r
6904         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6905         if (buttonDesc[0].hwnd) {\r
6906           SetFocus(buttonDesc[0].hwnd);\r
6907         } else {\r
6908           SetFocus(hwndMain);\r
6909         }\r
6910       } else {\r
6911         /* unshifted */\r
6912         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6913       }\r
6914     } else {\r
6915       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6916       JAWS_DELETE( SetFocus(hInput); )\r
6917       SendMessage(hInput, message, wParam, lParam);\r
6918     }\r
6919     return 0;\r
6920    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6921    lParam = -1;\r
6922   case WM_RBUTTONDOWN:\r
6923     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6924       /* Move selection here if it was empty */\r
6925       POINT pt;\r
6926       pt.x = LOWORD(lParam);\r
6927       pt.y = HIWORD(lParam);\r
6928       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6929       if (sel.cpMin == sel.cpMax) {\r
6930         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6931         sel.cpMax = sel.cpMin;\r
6932         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6933       }\r
6934       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6935 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6936       POINT pt;\r
6937       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6938       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6939       if (sel.cpMin == sel.cpMax) {\r
6940         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6941         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6942       }\r
6943       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6944         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6945       }\r
6946       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6947       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6948       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6949       MenuPopup(hwnd, pt, hmenu, -1);\r
6950 }\r
6951     }\r
6952     return 0;\r
6953   case WM_RBUTTONUP:\r
6954     if (GetKeyState(VK_SHIFT) & ~1) {\r
6955       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6956         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6957     }\r
6958     return 0;\r
6959   case WM_PASTE:\r
6960     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6961     SetFocus(hInput);\r
6962     return SendMessage(hInput, message, wParam, lParam);\r
6963   case WM_MBUTTONDOWN:\r
6964     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6965   case WM_COMMAND:\r
6966     switch (LOWORD(wParam)) {\r
6967     case IDM_QuickPaste:\r
6968       {\r
6969         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6970         if (sel.cpMin == sel.cpMax) {\r
6971           MessageBeep(MB_ICONEXCLAMATION);\r
6972           return 0;\r
6973         }\r
6974         SendMessage(hwnd, WM_COPY, 0, 0);\r
6975         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6976         SendMessage(hInput, WM_PASTE, 0, 0);\r
6977         SetFocus(hInput);\r
6978         return 0;\r
6979       }\r
6980     case IDM_Cut:\r
6981       SendMessage(hwnd, WM_CUT, 0, 0);\r
6982       return 0;\r
6983     case IDM_Paste:\r
6984       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6985       return 0;\r
6986     case IDM_Copy:\r
6987       SendMessage(hwnd, WM_COPY, 0, 0);\r
6988       return 0;\r
6989     default:\r
6990       {\r
6991         int i = LOWORD(wParam) - IDM_CommandX;\r
6992         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6993             icsTextMenuEntry[i].command != NULL) {\r
6994           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6995                    icsTextMenuEntry[i].getname,\r
6996                    icsTextMenuEntry[i].immediate);\r
6997           return 0;\r
6998         }\r
6999       }\r
7000       break;\r
7001     }\r
7002     break;\r
7003   }\r
7004   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7005 }\r
7006 \r
7007 WNDPROC consoleInputWindowProc;\r
7008 \r
7009 LRESULT CALLBACK\r
7010 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7011 {\r
7012   char buf[MSG_SIZ];\r
7013   char *p;\r
7014   static BOOL sendNextChar = FALSE;\r
7015   static BOOL quoteNextChar = FALSE;\r
7016   InputSource *is = consoleInputSource;\r
7017   CHARFORMAT cf;\r
7018   CHARRANGE sel;\r
7019 \r
7020   switch (message) {\r
7021   case WM_CHAR:\r
7022     if (!appData.localLineEditing || sendNextChar) {\r
7023       is->buf[0] = (CHAR) wParam;\r
7024       is->count = 1;\r
7025       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7026       sendNextChar = FALSE;\r
7027       return 0;\r
7028     }\r
7029     if (quoteNextChar) {\r
7030       buf[0] = (char) wParam;\r
7031       buf[1] = NULLCHAR;\r
7032       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7033       quoteNextChar = FALSE;\r
7034       return 0;\r
7035     }\r
7036     switch (wParam) {\r
7037     case '\r':   /* Enter key */\r
7038       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7039       if (consoleEcho) SaveInHistory(is->buf);\r
7040       is->buf[is->count++] = '\n';\r
7041       is->buf[is->count] = NULLCHAR;\r
7042       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7043       if (consoleEcho) {\r
7044         ConsoleOutput(is->buf, is->count, TRUE);\r
7045       } else if (appData.localLineEditing) {\r
7046         ConsoleOutput("\n", 1, TRUE);\r
7047       }\r
7048       /* fall thru */\r
7049     case '\033': /* Escape key */\r
7050       SetWindowText(hwnd, "");\r
7051       cf.cbSize = sizeof(CHARFORMAT);\r
7052       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7053       if (consoleEcho) {\r
7054         cf.crTextColor = textAttribs[ColorNormal].color;\r
7055       } else {\r
7056         cf.crTextColor = COLOR_ECHOOFF;\r
7057       }\r
7058       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7059       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7060       return 0;\r
7061     case '\t':   /* Tab key */\r
7062       if (GetKeyState(VK_SHIFT) < 0) {\r
7063         /* shifted */\r
7064         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7065       } else {\r
7066         /* unshifted */\r
7067         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7068         if (buttonDesc[0].hwnd) {\r
7069           SetFocus(buttonDesc[0].hwnd);\r
7070         } else {\r
7071           SetFocus(hwndMain);\r
7072         }\r
7073       }\r
7074       return 0;\r
7075     case '\023': /* Ctrl+S */\r
7076       sendNextChar = TRUE;\r
7077       return 0;\r
7078     case '\021': /* Ctrl+Q */\r
7079       quoteNextChar = TRUE;\r
7080       return 0;\r
7081     JAWS_REPLAY\r
7082     default:\r
7083       break;\r
7084     }\r
7085     break;\r
7086   case WM_KEYDOWN:\r
7087     switch (wParam) {\r
7088     case VK_UP:\r
7089       GetWindowText(hwnd, buf, MSG_SIZ);\r
7090       p = PrevInHistory(buf);\r
7091       if (p != NULL) {\r
7092         SetWindowText(hwnd, p);\r
7093         sel.cpMin = 999999;\r
7094         sel.cpMax = 999999;\r
7095         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7096         return 0;\r
7097       }\r
7098       break;\r
7099     case VK_DOWN:\r
7100       p = NextInHistory();\r
7101       if (p != NULL) {\r
7102         SetWindowText(hwnd, p);\r
7103         sel.cpMin = 999999;\r
7104         sel.cpMax = 999999;\r
7105         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7106         return 0;\r
7107       }\r
7108       break;\r
7109     case VK_HOME:\r
7110     case VK_END:\r
7111       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7112       /* fall thru */\r
7113     case VK_PRIOR:\r
7114     case VK_NEXT:\r
7115       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7116       return 0;\r
7117     }\r
7118     break;\r
7119   case WM_MBUTTONDOWN:\r
7120     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7121       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7122     break;\r
7123   case WM_RBUTTONUP:\r
7124     if (GetKeyState(VK_SHIFT) & ~1) {\r
7125       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7126         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7127     } else {\r
7128       POINT pt;\r
7129       HMENU hmenu;\r
7130       hmenu = LoadMenu(hInst, "InputMenu");\r
7131       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7132       if (sel.cpMin == sel.cpMax) {\r
7133         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7134         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7135       }\r
7136       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7137         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7138       }\r
7139       pt.x = LOWORD(lParam);\r
7140       pt.y = HIWORD(lParam);\r
7141       MenuPopup(hwnd, pt, hmenu, -1);\r
7142     }\r
7143     return 0;\r
7144   case WM_COMMAND:\r
7145     switch (LOWORD(wParam)) { \r
7146     case IDM_Undo:\r
7147       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7148       return 0;\r
7149     case IDM_SelectAll:\r
7150       sel.cpMin = 0;\r
7151       sel.cpMax = -1; /*999999?*/\r
7152       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7153       return 0;\r
7154     case IDM_Cut:\r
7155       SendMessage(hwnd, WM_CUT, 0, 0);\r
7156       return 0;\r
7157     case IDM_Paste:\r
7158       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7159       return 0;\r
7160     case IDM_Copy:\r
7161       SendMessage(hwnd, WM_COPY, 0, 0);\r
7162       return 0;\r
7163     }\r
7164     break;\r
7165   }\r
7166   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7167 }\r
7168 \r
7169 #define CO_MAX  100000\r
7170 #define CO_TRIM   1000\r
7171 \r
7172 LRESULT CALLBACK\r
7173 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7174 {\r
7175   static SnapData sd;\r
7176   HWND hText, hInput;\r
7177   RECT rect;\r
7178   static int sizeX, sizeY;\r
7179   int newSizeX, newSizeY;\r
7180   MINMAXINFO *mmi;\r
7181   WORD wMask;\r
7182 \r
7183   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7184   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7185 \r
7186   switch (message) {\r
7187   case WM_NOTIFY:\r
7188     if (((NMHDR*)lParam)->code == EN_LINK)\r
7189     {\r
7190       ENLINK *pLink = (ENLINK*)lParam;\r
7191       if (pLink->msg == WM_LBUTTONUP)\r
7192       {\r
7193         TEXTRANGE tr;\r
7194 \r
7195         tr.chrg = pLink->chrg;\r
7196         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7197         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7198         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7199         free(tr.lpstrText);\r
7200       }\r
7201     }\r
7202     break;\r
7203   case WM_INITDIALOG: /* message: initialize dialog box */\r
7204     hwndConsole = hDlg;\r
7205     SetFocus(hInput);\r
7206     consoleTextWindowProc = (WNDPROC)\r
7207       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7208     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7209     consoleInputWindowProc = (WNDPROC)\r
7210       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7211     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7212     Colorize(ColorNormal, TRUE);\r
7213     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7214     ChangedConsoleFont();\r
7215     GetClientRect(hDlg, &rect);\r
7216     sizeX = rect.right;\r
7217     sizeY = rect.bottom;\r
7218     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7219         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7220       WINDOWPLACEMENT wp;\r
7221       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7222       wp.length = sizeof(WINDOWPLACEMENT);\r
7223       wp.flags = 0;\r
7224       wp.showCmd = SW_SHOW;\r
7225       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7226       wp.rcNormalPosition.left = wpConsole.x;\r
7227       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7228       wp.rcNormalPosition.top = wpConsole.y;\r
7229       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7230       SetWindowPlacement(hDlg, &wp);\r
7231     }\r
7232 \r
7233    // [HGM] Chessknight's change 2004-07-13\r
7234    else { /* Determine Defaults */\r
7235        WINDOWPLACEMENT wp;\r
7236        wpConsole.x = wpMain.width + 1;\r
7237        wpConsole.y = wpMain.y;\r
7238        wpConsole.width = screenWidth -  wpMain.width;\r
7239        wpConsole.height = wpMain.height;\r
7240        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7241        wp.length = sizeof(WINDOWPLACEMENT);\r
7242        wp.flags = 0;\r
7243        wp.showCmd = SW_SHOW;\r
7244        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7245        wp.rcNormalPosition.left = wpConsole.x;\r
7246        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7247        wp.rcNormalPosition.top = wpConsole.y;\r
7248        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7249        SetWindowPlacement(hDlg, &wp);\r
7250     }\r
7251 \r
7252    // Allow hText to highlight URLs and send notifications on them\r
7253    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7254    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7255    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7256    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7257 \r
7258     return FALSE;\r
7259 \r
7260   case WM_SETFOCUS:\r
7261     SetFocus(hInput);\r
7262     return 0;\r
7263 \r
7264   case WM_CLOSE:\r
7265     ExitEvent(0);\r
7266     /* not reached */\r
7267     break;\r
7268 \r
7269   case WM_SIZE:\r
7270     if (IsIconic(hDlg)) break;\r
7271     newSizeX = LOWORD(lParam);\r
7272     newSizeY = HIWORD(lParam);\r
7273     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7274       RECT rectText, rectInput;\r
7275       POINT pt;\r
7276       int newTextHeight, newTextWidth;\r
7277       GetWindowRect(hText, &rectText);\r
7278       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7279       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7280       if (newTextHeight < 0) {\r
7281         newSizeY += -newTextHeight;\r
7282         newTextHeight = 0;\r
7283       }\r
7284       SetWindowPos(hText, NULL, 0, 0,\r
7285         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7286       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7287       pt.x = rectInput.left;\r
7288       pt.y = rectInput.top + newSizeY - sizeY;\r
7289       ScreenToClient(hDlg, &pt);\r
7290       SetWindowPos(hInput, NULL, \r
7291         pt.x, pt.y, /* needs client coords */   \r
7292         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7293         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7294     }\r
7295     sizeX = newSizeX;\r
7296     sizeY = newSizeY;\r
7297     break;\r
7298 \r
7299   case WM_GETMINMAXINFO:\r
7300     /* Prevent resizing window too small */\r
7301     mmi = (MINMAXINFO *) lParam;\r
7302     mmi->ptMinTrackSize.x = 100;\r
7303     mmi->ptMinTrackSize.y = 100;\r
7304     break;\r
7305 \r
7306   /* [AS] Snapping */\r
7307   case WM_ENTERSIZEMOVE:\r
7308     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7309 \r
7310   case WM_SIZING:\r
7311     return OnSizing( &sd, hDlg, wParam, lParam );\r
7312 \r
7313   case WM_MOVING:\r
7314     return OnMoving( &sd, hDlg, wParam, lParam );\r
7315 \r
7316   case WM_EXITSIZEMOVE:\r
7317         UpdateICSWidth(hText);\r
7318     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7319   }\r
7320 \r
7321   return DefWindowProc(hDlg, message, wParam, lParam);\r
7322 }\r
7323 \r
7324 \r
7325 VOID\r
7326 ConsoleCreate()\r
7327 {\r
7328   HWND hCons;\r
7329   if (hwndConsole) return;\r
7330   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7331   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7332 }\r
7333 \r
7334 \r
7335 VOID\r
7336 ConsoleOutput(char* data, int length, int forceVisible)\r
7337 {\r
7338   HWND hText;\r
7339   int trim, exlen;\r
7340   char *p, *q;\r
7341   char buf[CO_MAX+1];\r
7342   POINT pEnd;\r
7343   RECT rect;\r
7344   static int delayLF = 0;\r
7345   CHARRANGE savesel, sel;\r
7346 \r
7347   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7348   p = data;\r
7349   q = buf;\r
7350   if (delayLF) {\r
7351     *q++ = '\r';\r
7352     *q++ = '\n';\r
7353     delayLF = 0;\r
7354   }\r
7355   while (length--) {\r
7356     if (*p == '\n') {\r
7357       if (*++p) {\r
7358         *q++ = '\r';\r
7359         *q++ = '\n';\r
7360       } else {\r
7361         delayLF = 1;\r
7362       }\r
7363     } else if (*p == '\007') {\r
7364        MyPlaySound(&sounds[(int)SoundBell]);\r
7365        p++;\r
7366     } else {\r
7367       *q++ = *p++;\r
7368     }\r
7369   }\r
7370   *q = NULLCHAR;\r
7371   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7372   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7373   /* Save current selection */\r
7374   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7375   exlen = GetWindowTextLength(hText);\r
7376   /* Find out whether current end of text is visible */\r
7377   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7378   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7379   /* Trim existing text if it's too long */\r
7380   if (exlen + (q - buf) > CO_MAX) {\r
7381     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7382     sel.cpMin = 0;\r
7383     sel.cpMax = trim;\r
7384     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7385     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7386     exlen -= trim;\r
7387     savesel.cpMin -= trim;\r
7388     savesel.cpMax -= trim;\r
7389     if (exlen < 0) exlen = 0;\r
7390     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7391     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7392   }\r
7393   /* Append the new text */\r
7394   sel.cpMin = exlen;\r
7395   sel.cpMax = exlen;\r
7396   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7397   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7398   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7399   if (forceVisible || exlen == 0 ||\r
7400       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7401        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7402     /* Scroll to make new end of text visible if old end of text\r
7403        was visible or new text is an echo of user typein */\r
7404     sel.cpMin = 9999999;\r
7405     sel.cpMax = 9999999;\r
7406     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7407     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7408     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7409     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7410   }\r
7411   if (savesel.cpMax == exlen || forceVisible) {\r
7412     /* Move insert point to new end of text if it was at the old\r
7413        end of text or if the new text is an echo of user typein */\r
7414     sel.cpMin = 9999999;\r
7415     sel.cpMax = 9999999;\r
7416     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7417   } else {\r
7418     /* Restore previous selection */\r
7419     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7420   }\r
7421   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7422 }\r
7423 \r
7424 /*---------*/\r
7425 \r
7426 \r
7427 void\r
7428 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7429 {\r
7430   char buf[100];\r
7431   char *str;\r
7432   COLORREF oldFg, oldBg;\r
7433   HFONT oldFont;\r
7434   RECT rect;\r
7435 \r
7436   if(copyNumber > 1)
7437     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7438 \r
7439   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7440   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7441   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7442 \r
7443   rect.left = x;\r
7444   rect.right = x + squareSize;\r
7445   rect.top  = y;\r
7446   rect.bottom = y + squareSize;\r
7447   str = buf;\r
7448 \r
7449   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7450                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7451              y, ETO_CLIPPED|ETO_OPAQUE,\r
7452              &rect, str, strlen(str), NULL);\r
7453 \r
7454   (void) SetTextColor(hdc, oldFg);\r
7455   (void) SetBkColor(hdc, oldBg);\r
7456   (void) SelectObject(hdc, oldFont);\r
7457 }\r
7458 \r
7459 void\r
7460 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7461               RECT *rect, char *color, char *flagFell)\r
7462 {\r
7463   char buf[100];\r
7464   char *str;\r
7465   COLORREF oldFg, oldBg;\r
7466   HFONT oldFont;\r
7467 \r
7468   if (appData.clockMode) {\r
7469     if (tinyLayout)\r
7470       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7471     else\r
7472       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7473     str = buf;\r
7474   } else {\r
7475     str = color;\r
7476   }\r
7477 \r
7478   if (highlight) {\r
7479     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7480     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7481   } else {\r
7482     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7483     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7484   }\r
7485   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7486 \r
7487   JAWS_SILENCE\r
7488 \r
7489   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7490              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7491              rect, str, strlen(str), NULL);\r
7492   if(logoHeight > 0 && appData.clockMode) {\r
7493       RECT r;\r
7494       str += strlen(color)+2;\r
7495       r.top = rect->top + logoHeight/2;\r
7496       r.left = rect->left;\r
7497       r.right = rect->right;\r
7498       r.bottom = rect->bottom;\r
7499       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7500                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7501                  &r, str, strlen(str), NULL);\r
7502   }\r
7503   (void) SetTextColor(hdc, oldFg);\r
7504   (void) SetBkColor(hdc, oldBg);\r
7505   (void) SelectObject(hdc, oldFont);\r
7506 }\r
7507 \r
7508 \r
7509 int\r
7510 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7511            OVERLAPPED *ovl)\r
7512 {\r
7513   int ok, err;\r
7514 \r
7515   /* [AS]  */\r
7516   if( count <= 0 ) {\r
7517     if (appData.debugMode) {\r
7518       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7519     }\r
7520 \r
7521     return ERROR_INVALID_USER_BUFFER;\r
7522   }\r
7523 \r
7524   ResetEvent(ovl->hEvent);\r
7525   ovl->Offset = ovl->OffsetHigh = 0;\r
7526   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7527   if (ok) {\r
7528     err = NO_ERROR;\r
7529   } else {\r
7530     err = GetLastError();\r
7531     if (err == ERROR_IO_PENDING) {\r
7532       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7533       if (ok)\r
7534         err = NO_ERROR;\r
7535       else\r
7536         err = GetLastError();\r
7537     }\r
7538   }\r
7539   return err;\r
7540 }\r
7541 \r
7542 int\r
7543 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7544             OVERLAPPED *ovl)\r
7545 {\r
7546   int ok, err;\r
7547 \r
7548   ResetEvent(ovl->hEvent);\r
7549   ovl->Offset = ovl->OffsetHigh = 0;\r
7550   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7551   if (ok) {\r
7552     err = NO_ERROR;\r
7553   } else {\r
7554     err = GetLastError();\r
7555     if (err == ERROR_IO_PENDING) {\r
7556       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7557       if (ok)\r
7558         err = NO_ERROR;\r
7559       else\r
7560         err = GetLastError();\r
7561     }\r
7562   }\r
7563   return err;\r
7564 }\r
7565 \r
7566 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7567 void CheckForInputBufferFull( InputSource * is )\r
7568 {\r
7569     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7570         /* Look for end of line */\r
7571         char * p = is->buf;\r
7572         \r
7573         while( p < is->next && *p != '\n' ) {\r
7574             p++;\r
7575         }\r
7576 \r
7577         if( p >= is->next ) {\r
7578             if (appData.debugMode) {\r
7579                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7580             }\r
7581 \r
7582             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7583             is->count = (DWORD) -1;\r
7584             is->next = is->buf;\r
7585         }\r
7586     }\r
7587 }\r
7588 \r
7589 DWORD\r
7590 InputThread(LPVOID arg)\r
7591 {\r
7592   InputSource *is;\r
7593   OVERLAPPED ovl;\r
7594 \r
7595   is = (InputSource *) arg;\r
7596   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7597   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7598   while (is->hThread != NULL) {\r
7599     is->error = DoReadFile(is->hFile, is->next,\r
7600                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7601                            &is->count, &ovl);\r
7602     if (is->error == NO_ERROR) {\r
7603       is->next += is->count;\r
7604     } else {\r
7605       if (is->error == ERROR_BROKEN_PIPE) {\r
7606         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7607         is->count = 0;\r
7608       } else {\r
7609         is->count = (DWORD) -1;\r
7610         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7611         break; \r
7612       }\r
7613     }\r
7614 \r
7615     CheckForInputBufferFull( is );\r
7616 \r
7617     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7618 \r
7619     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7620 \r
7621     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7622   }\r
7623 \r
7624   CloseHandle(ovl.hEvent);\r
7625   CloseHandle(is->hFile);\r
7626 \r
7627   if (appData.debugMode) {\r
7628     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7629   }\r
7630 \r
7631   return 0;\r
7632 }\r
7633 \r
7634 \r
7635 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7636 DWORD\r
7637 NonOvlInputThread(LPVOID arg)\r
7638 {\r
7639   InputSource *is;\r
7640   char *p, *q;\r
7641   int i;\r
7642   char prev;\r
7643 \r
7644   is = (InputSource *) arg;\r
7645   while (is->hThread != NULL) {\r
7646     is->error = ReadFile(is->hFile, is->next,\r
7647                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7648                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7649     if (is->error == NO_ERROR) {\r
7650       /* Change CRLF to LF */\r
7651       if (is->next > is->buf) {\r
7652         p = is->next - 1;\r
7653         i = is->count + 1;\r
7654       } else {\r
7655         p = is->next;\r
7656         i = is->count;\r
7657       }\r
7658       q = p;\r
7659       prev = NULLCHAR;\r
7660       while (i > 0) {\r
7661         if (prev == '\r' && *p == '\n') {\r
7662           *(q-1) = '\n';\r
7663           is->count--;\r
7664         } else { \r
7665           *q++ = *p;\r
7666         }\r
7667         prev = *p++;\r
7668         i--;\r
7669       }\r
7670       *q = NULLCHAR;\r
7671       is->next = q;\r
7672     } else {\r
7673       if (is->error == ERROR_BROKEN_PIPE) {\r
7674         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7675         is->count = 0; \r
7676       } else {\r
7677         is->count = (DWORD) -1;\r
7678       }\r
7679     }\r
7680 \r
7681     CheckForInputBufferFull( is );\r
7682 \r
7683     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7684 \r
7685     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7686 \r
7687     if (is->count < 0) break;  /* Quit on error */\r
7688   }\r
7689   CloseHandle(is->hFile);\r
7690   return 0;\r
7691 }\r
7692 \r
7693 DWORD\r
7694 SocketInputThread(LPVOID arg)\r
7695 {\r
7696   InputSource *is;\r
7697 \r
7698   is = (InputSource *) arg;\r
7699   while (is->hThread != NULL) {\r
7700     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7701     if ((int)is->count == SOCKET_ERROR) {\r
7702       is->count = (DWORD) -1;\r
7703       is->error = WSAGetLastError();\r
7704     } else {\r
7705       is->error = NO_ERROR;\r
7706       is->next += is->count;\r
7707       if (is->count == 0 && is->second == is) {\r
7708         /* End of file on stderr; quit with no message */\r
7709         break;\r
7710       }\r
7711     }\r
7712     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7713 \r
7714     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7715 \r
7716     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7717   }\r
7718   return 0;\r
7719 }\r
7720 \r
7721 VOID\r
7722 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7723 {\r
7724   InputSource *is;\r
7725 \r
7726   is = (InputSource *) lParam;\r
7727   if (is->lineByLine) {\r
7728     /* Feed in lines one by one */\r
7729     char *p = is->buf;\r
7730     char *q = p;\r
7731     while (q < is->next) {\r
7732       if (*q++ == '\n') {\r
7733         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7734         p = q;\r
7735       }\r
7736     }\r
7737     \r
7738     /* Move any partial line to the start of the buffer */\r
7739     q = is->buf;\r
7740     while (p < is->next) {\r
7741       *q++ = *p++;\r
7742     }\r
7743     is->next = q;\r
7744 \r
7745     if (is->error != NO_ERROR || is->count == 0) {\r
7746       /* Notify backend of the error.  Note: If there was a partial\r
7747          line at the end, it is not flushed through. */\r
7748       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7749     }\r
7750   } else {\r
7751     /* Feed in the whole chunk of input at once */\r
7752     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7753     is->next = is->buf;\r
7754   }\r
7755 }\r
7756 \r
7757 /*---------------------------------------------------------------------------*\\r
7758  *\r
7759  *  Menu enables. Used when setting various modes.\r
7760  *\r
7761 \*---------------------------------------------------------------------------*/\r
7762 \r
7763 typedef struct {\r
7764   int item;\r
7765   int flags;\r
7766 } Enables;\r
7767 \r
7768 VOID\r
7769 GreyRevert(Boolean grey)\r
7770 { // [HGM] vari: for retracting variations in local mode\r
7771   HMENU hmenu = GetMenu(hwndMain);\r
7772   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7773   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7774 }\r
7775 \r
7776 VOID\r
7777 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7778 {\r
7779   while (enab->item > 0) {\r
7780     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7781     enab++;\r
7782   }\r
7783 }\r
7784 \r
7785 Enables gnuEnables[] = {\r
7786   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7799 \r
7800   // Needed to switch from ncp to GNU mode on Engine Load\r
7801   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7802   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7803   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7806   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7815   { -1, -1 }\r
7816 };\r
7817 \r
7818 Enables icsEnables[] = {\r
7819   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7838   { -1, -1 }\r
7839 };\r
7840 \r
7841 #if ZIPPY\r
7842 Enables zippyEnables[] = {\r
7843   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7844   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7847   { -1, -1 }\r
7848 };\r
7849 #endif\r
7850 \r
7851 Enables ncpEnables[] = {\r
7852   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7861   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7862   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7874   { -1, -1 }\r
7875 };\r
7876 \r
7877 Enables trainingOnEnables[] = {\r
7878   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7887   { -1, -1 }\r
7888 };\r
7889 \r
7890 Enables trainingOffEnables[] = {\r
7891   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7892   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7893   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7894   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7895   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7900   { -1, -1 }\r
7901 };\r
7902 \r
7903 /* These modify either ncpEnables or gnuEnables */\r
7904 Enables cmailEnables[] = {\r
7905   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7907   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7908   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7912   { -1, -1 }\r
7913 };\r
7914 \r
7915 Enables machineThinkingEnables[] = {\r
7916   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7929 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7932   { -1, -1 }\r
7933 };\r
7934 \r
7935 Enables userThinkingEnables[] = {\r
7936   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7940   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7941   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7942   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7943   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7949 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7952   { -1, -1 }\r
7953 };\r
7954 \r
7955 /*---------------------------------------------------------------------------*\\r
7956  *\r
7957  *  Front-end interface functions exported by XBoard.\r
7958  *  Functions appear in same order as prototypes in frontend.h.\r
7959  * \r
7960 \*---------------------------------------------------------------------------*/\r
7961 VOID\r
7962 CheckMark(UINT item, int state)\r
7963 {\r
7964     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7965 }\r
7966 \r
7967 VOID\r
7968 ModeHighlight()\r
7969 {\r
7970   static UINT prevChecked = 0;\r
7971   static int prevPausing = 0;\r
7972   UINT nowChecked;\r
7973 \r
7974   if (pausing != prevPausing) {\r
7975     prevPausing = pausing;\r
7976     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7977                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7978     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7979   }\r
7980 \r
7981   switch (gameMode) {\r
7982   case BeginningOfGame:\r
7983     if (appData.icsActive)\r
7984       nowChecked = IDM_IcsClient;\r
7985     else if (appData.noChessProgram)\r
7986       nowChecked = IDM_EditGame;\r
7987     else\r
7988       nowChecked = IDM_MachineBlack;\r
7989     break;\r
7990   case MachinePlaysBlack:\r
7991     nowChecked = IDM_MachineBlack;\r
7992     break;\r
7993   case MachinePlaysWhite:\r
7994     nowChecked = IDM_MachineWhite;\r
7995     break;\r
7996   case TwoMachinesPlay:\r
7997     nowChecked = IDM_TwoMachines;\r
7998     break;\r
7999   case AnalyzeMode:\r
8000     nowChecked = IDM_AnalysisMode;\r
8001     break;\r
8002   case AnalyzeFile:\r
8003     nowChecked = IDM_AnalyzeFile;\r
8004     break;\r
8005   case EditGame:\r
8006     nowChecked = IDM_EditGame;\r
8007     break;\r
8008   case PlayFromGameFile:\r
8009     nowChecked = IDM_LoadGame;\r
8010     break;\r
8011   case EditPosition:\r
8012     nowChecked = IDM_EditPosition;\r
8013     break;\r
8014   case Training:\r
8015     nowChecked = IDM_Training;\r
8016     break;\r
8017   case IcsPlayingWhite:\r
8018   case IcsPlayingBlack:\r
8019   case IcsObserving:\r
8020   case IcsIdle:\r
8021     nowChecked = IDM_IcsClient;\r
8022     break;\r
8023   default:\r
8024   case EndOfGame:\r
8025     nowChecked = 0;\r
8026     break;\r
8027   }\r
8028   CheckMark(prevChecked, MF_UNCHECKED);\r
8029   CheckMark(nowChecked, MF_CHECKED);\r
8030   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8031 \r
8032   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8033     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8034                           MF_BYCOMMAND|MF_ENABLED);\r
8035   } else {\r
8036     (void) EnableMenuItem(GetMenu(hwndMain), \r
8037                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8038   }\r
8039 \r
8040   prevChecked = nowChecked;\r
8041 \r
8042   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8043   if (appData.icsActive) {\r
8044        if (appData.icsEngineAnalyze) {\r
8045                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8046        } else {\r
8047                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8048        }\r
8049   }\r
8050   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8051 }\r
8052 \r
8053 VOID\r
8054 SetICSMode()\r
8055 {\r
8056   HMENU hmenu = GetMenu(hwndMain);\r
8057   SetMenuEnables(hmenu, icsEnables);\r
8058   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8059     MF_BYCOMMAND|MF_ENABLED);\r
8060 #if ZIPPY\r
8061   if (appData.zippyPlay) {\r
8062     SetMenuEnables(hmenu, zippyEnables);\r
8063     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8064          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8065           MF_BYCOMMAND|MF_ENABLED);\r
8066   }\r
8067 #endif\r
8068 }\r
8069 \r
8070 VOID\r
8071 SetGNUMode()\r
8072 {\r
8073   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8074 }\r
8075 \r
8076 VOID\r
8077 SetNCPMode()\r
8078 {\r
8079   HMENU hmenu = GetMenu(hwndMain);\r
8080   SetMenuEnables(hmenu, ncpEnables);\r
8081     DrawMenuBar(hwndMain);\r
8082 }\r
8083 \r
8084 VOID\r
8085 SetCmailMode()\r
8086 {\r
8087   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8088 }\r
8089 \r
8090 VOID \r
8091 SetTrainingModeOn()\r
8092 {\r
8093   int i;\r
8094   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8095   for (i = 0; i < N_BUTTONS; i++) {\r
8096     if (buttonDesc[i].hwnd != NULL)\r
8097       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8098   }\r
8099   CommentPopDown();\r
8100 }\r
8101 \r
8102 VOID SetTrainingModeOff()\r
8103 {\r
8104   int i;\r
8105   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8106   for (i = 0; i < N_BUTTONS; i++) {\r
8107     if (buttonDesc[i].hwnd != NULL)\r
8108       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8109   }\r
8110 }\r
8111 \r
8112 \r
8113 VOID\r
8114 SetUserThinkingEnables()\r
8115 {\r
8116   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8117 }\r
8118 \r
8119 VOID\r
8120 SetMachineThinkingEnables()\r
8121 {\r
8122   HMENU hMenu = GetMenu(hwndMain);\r
8123   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8124 \r
8125   SetMenuEnables(hMenu, machineThinkingEnables);\r
8126 \r
8127   if (gameMode == MachinePlaysBlack) {\r
8128     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8129   } else if (gameMode == MachinePlaysWhite) {\r
8130     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8131   } else if (gameMode == TwoMachinesPlay) {\r
8132     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8133   }\r
8134 }\r
8135 \r
8136 \r
8137 VOID\r
8138 DisplayTitle(char *str)\r
8139 {\r
8140   char title[MSG_SIZ], *host;\r
8141   if (str[0] != NULLCHAR) {\r
8142     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8143   } else if (appData.icsActive) {\r
8144     if (appData.icsCommPort[0] != NULLCHAR)\r
8145       host = "ICS";\r
8146     else \r
8147       host = appData.icsHost;\r
8148       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8149   } else if (appData.noChessProgram) {\r
8150     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8151   } else {\r
8152     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8153     strcat(title, ": ");\r
8154     strcat(title, first.tidy);\r
8155   }\r
8156   SetWindowText(hwndMain, title);\r
8157 }\r
8158 \r
8159 \r
8160 VOID\r
8161 DisplayMessage(char *str1, char *str2)\r
8162 {\r
8163   HDC hdc;\r
8164   HFONT oldFont;\r
8165   int remain = MESSAGE_TEXT_MAX - 1;\r
8166   int len;\r
8167 \r
8168   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8169   messageText[0] = NULLCHAR;\r
8170   if (*str1) {\r
8171     len = strlen(str1);\r
8172     if (len > remain) len = remain;\r
8173     strncpy(messageText, str1, len);\r
8174     messageText[len] = NULLCHAR;\r
8175     remain -= len;\r
8176   }\r
8177   if (*str2 && remain >= 2) {\r
8178     if (*str1) {\r
8179       strcat(messageText, "  ");\r
8180       remain -= 2;\r
8181     }\r
8182     len = strlen(str2);\r
8183     if (len > remain) len = remain;\r
8184     strncat(messageText, str2, len);\r
8185   }\r
8186   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8187   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8188 \r
8189   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8190 \r
8191   SAYMACHINEMOVE();\r
8192 \r
8193   hdc = GetDC(hwndMain);\r
8194   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8195   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8196              &messageRect, messageText, strlen(messageText), NULL);\r
8197   (void) SelectObject(hdc, oldFont);\r
8198   (void) ReleaseDC(hwndMain, hdc);\r
8199 }\r
8200 \r
8201 VOID\r
8202 DisplayError(char *str, int error)\r
8203 {\r
8204   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8205   int len;\r
8206 \r
8207   if (error == 0) {\r
8208     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8209   } else {\r
8210     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8211                         NULL, error, LANG_NEUTRAL,\r
8212                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8213     if (len > 0) {\r
8214       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8215     } else {\r
8216       ErrorMap *em = errmap;\r
8217       while (em->err != 0 && em->err != error) em++;\r
8218       if (em->err != 0) {\r
8219         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8220       } else {\r
8221         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8222       }\r
8223     }\r
8224   }\r
8225   \r
8226   ErrorPopUp(_("Error"), buf);\r
8227 }\r
8228 \r
8229 \r
8230 VOID\r
8231 DisplayMoveError(char *str)\r
8232 {\r
8233   fromX = fromY = -1;\r
8234   ClearHighlights();\r
8235   DrawPosition(FALSE, NULL);\r
8236   if (appData.popupMoveErrors) {\r
8237     ErrorPopUp(_("Error"), str);\r
8238   } else {\r
8239     DisplayMessage(str, "");\r
8240     moveErrorMessageUp = TRUE;\r
8241   }\r
8242 }\r
8243 \r
8244 VOID\r
8245 DisplayFatalError(char *str, int error, int exitStatus)\r
8246 {\r
8247   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8248   int len;\r
8249   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8250 \r
8251   if (error != 0) {\r
8252     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8253                         NULL, error, LANG_NEUTRAL,\r
8254                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8255     if (len > 0) {\r
8256       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8257     } else {\r
8258       ErrorMap *em = errmap;\r
8259       while (em->err != 0 && em->err != error) em++;\r
8260       if (em->err != 0) {\r
8261         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8262       } else {\r
8263         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8264       }\r
8265     }\r
8266     str = buf;\r
8267   }\r
8268   if (appData.debugMode) {\r
8269     fprintf(debugFP, "%s: %s\n", label, str);\r
8270   }\r
8271   if (appData.popupExitMessage) {\r
8272     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8273                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8274   }\r
8275   ExitEvent(exitStatus);\r
8276 }\r
8277 \r
8278 \r
8279 VOID\r
8280 DisplayInformation(char *str)\r
8281 {\r
8282   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8283 }\r
8284 \r
8285 \r
8286 VOID\r
8287 DisplayNote(char *str)\r
8288 {\r
8289   ErrorPopUp(_("Note"), str);\r
8290 }\r
8291 \r
8292 \r
8293 typedef struct {\r
8294   char *title, *question, *replyPrefix;\r
8295   ProcRef pr;\r
8296 } QuestionParams;\r
8297 \r
8298 LRESULT CALLBACK\r
8299 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8300 {\r
8301   static QuestionParams *qp;\r
8302   char reply[MSG_SIZ];\r
8303   int len, err;\r
8304 \r
8305   switch (message) {\r
8306   case WM_INITDIALOG:\r
8307     qp = (QuestionParams *) lParam;\r
8308     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8309     Translate(hDlg, DLG_Question);\r
8310     SetWindowText(hDlg, qp->title);\r
8311     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8312     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8313     return FALSE;\r
8314 \r
8315   case WM_COMMAND:\r
8316     switch (LOWORD(wParam)) {\r
8317     case IDOK:\r
8318       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8319       if (*reply) strcat(reply, " ");\r
8320       len = strlen(reply);\r
8321       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8322       strcat(reply, "\n");\r
8323       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8324       EndDialog(hDlg, TRUE);\r
8325       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8326       return TRUE;\r
8327     case IDCANCEL:\r
8328       EndDialog(hDlg, FALSE);\r
8329       return TRUE;\r
8330     default:\r
8331       break;\r
8332     }\r
8333     break;\r
8334   }\r
8335   return FALSE;\r
8336 }\r
8337 \r
8338 VOID\r
8339 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8340 {\r
8341     QuestionParams qp;\r
8342     FARPROC lpProc;\r
8343     \r
8344     qp.title = title;\r
8345     qp.question = question;\r
8346     qp.replyPrefix = replyPrefix;\r
8347     qp.pr = pr;\r
8348     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8349     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8350       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8351     FreeProcInstance(lpProc);\r
8352 }\r
8353 \r
8354 /* [AS] Pick FRC position */\r
8355 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8356 {\r
8357     static int * lpIndexFRC;\r
8358     BOOL index_is_ok;\r
8359     char buf[16];\r
8360 \r
8361     switch( message )\r
8362     {\r
8363     case WM_INITDIALOG:\r
8364         lpIndexFRC = (int *) lParam;\r
8365 \r
8366         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8367         Translate(hDlg, DLG_NewGameFRC);\r
8368 \r
8369         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8370         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8371         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8372         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8373 \r
8374         break;\r
8375 \r
8376     case WM_COMMAND:\r
8377         switch( LOWORD(wParam) ) {\r
8378         case IDOK:\r
8379             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8380             EndDialog( hDlg, 0 );\r
8381             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8382             return TRUE;\r
8383         case IDCANCEL:\r
8384             EndDialog( hDlg, 1 );   \r
8385             return TRUE;\r
8386         case IDC_NFG_Edit:\r
8387             if( HIWORD(wParam) == EN_CHANGE ) {\r
8388                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8389 \r
8390                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8391             }\r
8392             return TRUE;\r
8393         case IDC_NFG_Random:\r
8394           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8395             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8396             return TRUE;\r
8397         }\r
8398 \r
8399         break;\r
8400     }\r
8401 \r
8402     return FALSE;\r
8403 }\r
8404 \r
8405 int NewGameFRC()\r
8406 {\r
8407     int result;\r
8408     int index = appData.defaultFrcPosition;\r
8409     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8410 \r
8411     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8412 \r
8413     if( result == 0 ) {\r
8414         appData.defaultFrcPosition = index;\r
8415     }\r
8416 \r
8417     return result;\r
8418 }\r
8419 \r
8420 /* [AS] Game list options. Refactored by HGM */\r
8421 \r
8422 HWND gameListOptionsDialog;\r
8423 \r
8424 // low-level front-end: clear text edit / list widget\r
8425 void\r
8426 GLT_ClearList()\r
8427 {\r
8428     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8429 }\r
8430 \r
8431 // low-level front-end: clear text edit / list widget\r
8432 void\r
8433 GLT_DeSelectList()\r
8434 {\r
8435     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8436 }\r
8437 \r
8438 // low-level front-end: append line to text edit / list widget\r
8439 void\r
8440 GLT_AddToList( char *name )\r
8441 {\r
8442     if( name != 0 ) {\r
8443             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8444     }\r
8445 }\r
8446 \r
8447 // low-level front-end: get line from text edit / list widget\r
8448 Boolean\r
8449 GLT_GetFromList( int index, char *name )\r
8450 {\r
8451     if( name != 0 ) {\r
8452             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8453                 return TRUE;\r
8454     }\r
8455     return FALSE;\r
8456 }\r
8457 \r
8458 void GLT_MoveSelection( HWND hDlg, int delta )\r
8459 {\r
8460     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8461     int idx2 = idx1 + delta;\r
8462     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8463 \r
8464     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8465         char buf[128];\r
8466 \r
8467         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8468         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8469         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8470         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8471     }\r
8472 }\r
8473 \r
8474 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8475 {\r
8476     switch( message )\r
8477     {\r
8478     case WM_INITDIALOG:\r
8479         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8480         \r
8481         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8482         Translate(hDlg, DLG_GameListOptions);\r
8483 \r
8484         /* Initialize list */\r
8485         GLT_TagsToList( lpUserGLT );\r
8486 \r
8487         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8488 \r
8489         break;\r
8490 \r
8491     case WM_COMMAND:\r
8492         switch( LOWORD(wParam) ) {\r
8493         case IDOK:\r
8494             GLT_ParseList();\r
8495             EndDialog( hDlg, 0 );\r
8496             return TRUE;\r
8497         case IDCANCEL:\r
8498             EndDialog( hDlg, 1 );\r
8499             return TRUE;\r
8500 \r
8501         case IDC_GLT_Default:\r
8502             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8503             return TRUE;\r
8504 \r
8505         case IDC_GLT_Restore:\r
8506             GLT_TagsToList( appData.gameListTags );\r
8507             return TRUE;\r
8508 \r
8509         case IDC_GLT_Up:\r
8510             GLT_MoveSelection( hDlg, -1 );\r
8511             return TRUE;\r
8512 \r
8513         case IDC_GLT_Down:\r
8514             GLT_MoveSelection( hDlg, +1 );\r
8515             return TRUE;\r
8516         }\r
8517 \r
8518         break;\r
8519     }\r
8520 \r
8521     return FALSE;\r
8522 }\r
8523 \r
8524 int GameListOptions()\r
8525 {\r
8526     int result;\r
8527     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8528 \r
8529       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8530 \r
8531     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8532 \r
8533     if( result == 0 ) {\r
8534         /* [AS] Memory leak here! */\r
8535         appData.gameListTags = strdup( lpUserGLT ); \r
8536     }\r
8537 \r
8538     return result;\r
8539 }\r
8540 \r
8541 VOID\r
8542 DisplayIcsInteractionTitle(char *str)\r
8543 {\r
8544   char consoleTitle[MSG_SIZ];\r
8545 \r
8546     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8547     SetWindowText(hwndConsole, consoleTitle);\r
8548 \r
8549     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8550       char buf[MSG_SIZ], *p = buf, *q;\r
8551         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8552       do {\r
8553         q = strchr(p, ';');\r
8554         if(q) *q++ = 0;\r
8555         if(*p) ChatPopUp(p);\r
8556       } while(p=q);\r
8557     }\r
8558 \r
8559     SetActiveWindow(hwndMain);\r
8560 }\r
8561 \r
8562 void\r
8563 DrawPosition(int fullRedraw, Board board)\r
8564 {\r
8565   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8566 }\r
8567 \r
8568 void NotifyFrontendLogin()\r
8569 {\r
8570         if (hwndConsole)\r
8571                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8572 }\r
8573 \r
8574 VOID\r
8575 ResetFrontEnd()\r
8576 {\r
8577   fromX = fromY = -1;\r
8578   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8579     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8580     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8581     dragInfo.lastpos = dragInfo.pos;\r
8582     dragInfo.start.x = dragInfo.start.y = -1;\r
8583     dragInfo.from = dragInfo.start;\r
8584     ReleaseCapture();\r
8585     DrawPosition(TRUE, NULL);\r
8586   }\r
8587   TagsPopDown();\r
8588 }\r
8589 \r
8590 \r
8591 VOID\r
8592 CommentPopUp(char *title, char *str)\r
8593 {\r
8594   HWND hwnd = GetActiveWindow();\r
8595   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8596   SAY(str);\r
8597   SetActiveWindow(hwnd);\r
8598 }\r
8599 \r
8600 VOID\r
8601 CommentPopDown(void)\r
8602 {\r
8603   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8604   if (commentDialog) {\r
8605     ShowWindow(commentDialog, SW_HIDE);\r
8606   }\r
8607   commentUp = FALSE;\r
8608 }\r
8609 \r
8610 VOID\r
8611 EditCommentPopUp(int index, char *title, char *str)\r
8612 {\r
8613   EitherCommentPopUp(index, title, str, TRUE);\r
8614 }\r
8615 \r
8616 \r
8617 VOID\r
8618 RingBell()\r
8619 {\r
8620   MyPlaySound(&sounds[(int)SoundMove]);\r
8621 }\r
8622 \r
8623 VOID PlayIcsWinSound()\r
8624 {\r
8625   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8626 }\r
8627 \r
8628 VOID PlayIcsLossSound()\r
8629 {\r
8630   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8631 }\r
8632 \r
8633 VOID PlayIcsDrawSound()\r
8634 {\r
8635   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8636 }\r
8637 \r
8638 VOID PlayIcsUnfinishedSound()\r
8639 {\r
8640   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8641 }\r
8642 \r
8643 VOID\r
8644 PlayAlarmSound()\r
8645 {\r
8646   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8647 }\r
8648 \r
8649 VOID\r
8650 PlayTellSound()\r
8651 {\r
8652   MyPlaySound(&textAttribs[ColorTell].sound);\r
8653 }\r
8654 \r
8655 \r
8656 VOID\r
8657 EchoOn()\r
8658 {\r
8659   HWND hInput;\r
8660   consoleEcho = TRUE;\r
8661   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8662   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8663   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8664 }\r
8665 \r
8666 \r
8667 VOID\r
8668 EchoOff()\r
8669 {\r
8670   CHARFORMAT cf;\r
8671   HWND hInput;\r
8672   consoleEcho = FALSE;\r
8673   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8674   /* This works OK: set text and background both to the same color */\r
8675   cf = consoleCF;\r
8676   cf.crTextColor = COLOR_ECHOOFF;\r
8677   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8678   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8679 }\r
8680 \r
8681 /* No Raw()...? */\r
8682 \r
8683 void Colorize(ColorClass cc, int continuation)\r
8684 {\r
8685   currentColorClass = cc;\r
8686   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8687   consoleCF.crTextColor = textAttribs[cc].color;\r
8688   consoleCF.dwEffects = textAttribs[cc].effects;\r
8689   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8690 }\r
8691 \r
8692 char *\r
8693 UserName()\r
8694 {\r
8695   static char buf[MSG_SIZ];\r
8696   DWORD bufsiz = MSG_SIZ;\r
8697 \r
8698   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8699         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8700   }\r
8701   if (!GetUserName(buf, &bufsiz)) {\r
8702     /*DisplayError("Error getting user name", GetLastError());*/\r
8703     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8704   }\r
8705   return buf;\r
8706 }\r
8707 \r
8708 char *\r
8709 HostName()\r
8710 {\r
8711   static char buf[MSG_SIZ];\r
8712   DWORD bufsiz = MSG_SIZ;\r
8713 \r
8714   if (!GetComputerName(buf, &bufsiz)) {\r
8715     /*DisplayError("Error getting host name", GetLastError());*/\r
8716     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8717   }\r
8718   return buf;\r
8719 }\r
8720 \r
8721 \r
8722 int\r
8723 ClockTimerRunning()\r
8724 {\r
8725   return clockTimerEvent != 0;\r
8726 }\r
8727 \r
8728 int\r
8729 StopClockTimer()\r
8730 {\r
8731   if (clockTimerEvent == 0) return FALSE;\r
8732   KillTimer(hwndMain, clockTimerEvent);\r
8733   clockTimerEvent = 0;\r
8734   return TRUE;\r
8735 }\r
8736 \r
8737 void\r
8738 StartClockTimer(long millisec)\r
8739 {\r
8740   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8741                              (UINT) millisec, NULL);\r
8742 }\r
8743 \r
8744 void\r
8745 DisplayWhiteClock(long timeRemaining, int highlight)\r
8746 {\r
8747   HDC hdc;\r
8748   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8749 \r
8750   if(appData.noGUI) return;\r
8751   hdc = GetDC(hwndMain);\r
8752   if (!IsIconic(hwndMain)) {\r
8753     DisplayAClock(hdc, timeRemaining, highlight, \r
8754                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8755   }\r
8756   if (highlight && iconCurrent == iconBlack) {\r
8757     iconCurrent = iconWhite;\r
8758     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8759     if (IsIconic(hwndMain)) {\r
8760       DrawIcon(hdc, 2, 2, iconCurrent);\r
8761     }\r
8762   }\r
8763   (void) ReleaseDC(hwndMain, hdc);\r
8764   if (hwndConsole)\r
8765     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8766 }\r
8767 \r
8768 void\r
8769 DisplayBlackClock(long timeRemaining, int highlight)\r
8770 {\r
8771   HDC hdc;\r
8772   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8773 \r
8774   if(appData.noGUI) return;\r
8775   hdc = GetDC(hwndMain);\r
8776   if (!IsIconic(hwndMain)) {\r
8777     DisplayAClock(hdc, timeRemaining, highlight, \r
8778                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8779   }\r
8780   if (highlight && iconCurrent == iconWhite) {\r
8781     iconCurrent = iconBlack;\r
8782     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8783     if (IsIconic(hwndMain)) {\r
8784       DrawIcon(hdc, 2, 2, iconCurrent);\r
8785     }\r
8786   }\r
8787   (void) ReleaseDC(hwndMain, hdc);\r
8788   if (hwndConsole)\r
8789     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8790 }\r
8791 \r
8792 \r
8793 int\r
8794 LoadGameTimerRunning()\r
8795 {\r
8796   return loadGameTimerEvent != 0;\r
8797 }\r
8798 \r
8799 int\r
8800 StopLoadGameTimer()\r
8801 {\r
8802   if (loadGameTimerEvent == 0) return FALSE;\r
8803   KillTimer(hwndMain, loadGameTimerEvent);\r
8804   loadGameTimerEvent = 0;\r
8805   return TRUE;\r
8806 }\r
8807 \r
8808 void\r
8809 StartLoadGameTimer(long millisec)\r
8810 {\r
8811   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8812                                 (UINT) millisec, NULL);\r
8813 }\r
8814 \r
8815 void\r
8816 AutoSaveGame()\r
8817 {\r
8818   char *defName;\r
8819   FILE *f;\r
8820   char fileTitle[MSG_SIZ];\r
8821 \r
8822   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8823   f = OpenFileDialog(hwndMain, "a", defName,\r
8824                      appData.oldSaveStyle ? "gam" : "pgn",\r
8825                      GAME_FILT, \r
8826                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8827   if (f != NULL) {\r
8828     SaveGame(f, 0, "");\r
8829     fclose(f);\r
8830   }\r
8831 }\r
8832 \r
8833 \r
8834 void\r
8835 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8836 {\r
8837   if (delayedTimerEvent != 0) {\r
8838     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8839       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8840     }\r
8841     KillTimer(hwndMain, delayedTimerEvent);\r
8842     delayedTimerEvent = 0;\r
8843     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8844     delayedTimerCallback();\r
8845   }\r
8846   delayedTimerCallback = cb;\r
8847   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8848                                 (UINT) millisec, NULL);\r
8849 }\r
8850 \r
8851 DelayedEventCallback\r
8852 GetDelayedEvent()\r
8853 {\r
8854   if (delayedTimerEvent) {\r
8855     return delayedTimerCallback;\r
8856   } else {\r
8857     return NULL;\r
8858   }\r
8859 }\r
8860 \r
8861 void\r
8862 CancelDelayedEvent()\r
8863 {\r
8864   if (delayedTimerEvent) {\r
8865     KillTimer(hwndMain, delayedTimerEvent);\r
8866     delayedTimerEvent = 0;\r
8867   }\r
8868 }\r
8869 \r
8870 DWORD GetWin32Priority(int nice)\r
8871 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8872 /*\r
8873 REALTIME_PRIORITY_CLASS     0x00000100\r
8874 HIGH_PRIORITY_CLASS         0x00000080\r
8875 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8876 NORMAL_PRIORITY_CLASS       0x00000020\r
8877 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8878 IDLE_PRIORITY_CLASS         0x00000040\r
8879 */\r
8880         if (nice < -15) return 0x00000080;\r
8881         if (nice < 0)   return 0x00008000;\r
8882         if (nice == 0)  return 0x00000020;\r
8883         if (nice < 15)  return 0x00004000;\r
8884         return 0x00000040;\r
8885 }\r
8886 \r
8887 void RunCommand(char *cmdLine)\r
8888 {\r
8889   /* Now create the child process. */\r
8890   STARTUPINFO siStartInfo;\r
8891   PROCESS_INFORMATION piProcInfo;\r
8892 \r
8893   siStartInfo.cb = sizeof(STARTUPINFO);\r
8894   siStartInfo.lpReserved = NULL;\r
8895   siStartInfo.lpDesktop = NULL;\r
8896   siStartInfo.lpTitle = NULL;\r
8897   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8898   siStartInfo.cbReserved2 = 0;\r
8899   siStartInfo.lpReserved2 = NULL;\r
8900   siStartInfo.hStdInput = NULL;\r
8901   siStartInfo.hStdOutput = NULL;\r
8902   siStartInfo.hStdError = NULL;\r
8903 \r
8904   CreateProcess(NULL,\r
8905                 cmdLine,           /* command line */\r
8906                 NULL,      /* process security attributes */\r
8907                 NULL,      /* primary thread security attrs */\r
8908                 TRUE,      /* handles are inherited */\r
8909                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8910                 NULL,      /* use parent's environment */\r
8911                 NULL,\r
8912                 &siStartInfo, /* STARTUPINFO pointer */\r
8913                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8914 \r
8915   CloseHandle(piProcInfo.hThread);\r
8916 }\r
8917 \r
8918 /* Start a child process running the given program.\r
8919    The process's standard output can be read from "from", and its\r
8920    standard input can be written to "to".\r
8921    Exit with fatal error if anything goes wrong.\r
8922    Returns an opaque pointer that can be used to destroy the process\r
8923    later.\r
8924 */\r
8925 int\r
8926 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8927 {\r
8928 #define BUFSIZE 4096\r
8929 \r
8930   HANDLE hChildStdinRd, hChildStdinWr,\r
8931     hChildStdoutRd, hChildStdoutWr;\r
8932   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8933   SECURITY_ATTRIBUTES saAttr;\r
8934   BOOL fSuccess;\r
8935   PROCESS_INFORMATION piProcInfo;\r
8936   STARTUPINFO siStartInfo;\r
8937   ChildProc *cp;\r
8938   char buf[MSG_SIZ];\r
8939   DWORD err;\r
8940 \r
8941   if (appData.debugMode) {\r
8942     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8943   }\r
8944 \r
8945   *pr = NoProc;\r
8946 \r
8947   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8948   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8949   saAttr.bInheritHandle = TRUE;\r
8950   saAttr.lpSecurityDescriptor = NULL;\r
8951 \r
8952   /*\r
8953    * The steps for redirecting child's STDOUT:\r
8954    *     1. Create anonymous pipe to be STDOUT for child.\r
8955    *     2. Create a noninheritable duplicate of read handle,\r
8956    *         and close the inheritable read handle.\r
8957    */\r
8958 \r
8959   /* Create a pipe for the child's STDOUT. */\r
8960   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8961     return GetLastError();\r
8962   }\r
8963 \r
8964   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8965   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8966                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8967                              FALSE,     /* not inherited */\r
8968                              DUPLICATE_SAME_ACCESS);\r
8969   if (! fSuccess) {\r
8970     return GetLastError();\r
8971   }\r
8972   CloseHandle(hChildStdoutRd);\r
8973 \r
8974   /*\r
8975    * The steps for redirecting child's STDIN:\r
8976    *     1. Create anonymous pipe to be STDIN for child.\r
8977    *     2. Create a noninheritable duplicate of write handle,\r
8978    *         and close the inheritable write handle.\r
8979    */\r
8980 \r
8981   /* Create a pipe for the child's STDIN. */\r
8982   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8983     return GetLastError();\r
8984   }\r
8985 \r
8986   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8987   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8988                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8989                              FALSE,     /* not inherited */\r
8990                              DUPLICATE_SAME_ACCESS);\r
8991   if (! fSuccess) {\r
8992     return GetLastError();\r
8993   }\r
8994   CloseHandle(hChildStdinWr);\r
8995 \r
8996   /* Arrange to (1) look in dir for the child .exe file, and\r
8997    * (2) have dir be the child's working directory.  Interpret\r
8998    * dir relative to the directory WinBoard loaded from. */\r
8999   GetCurrentDirectory(MSG_SIZ, buf);\r
9000   SetCurrentDirectory(installDir);\r
9001   SetCurrentDirectory(dir);\r
9002 \r
9003   /* Now create the child process. */\r
9004 \r
9005   siStartInfo.cb = sizeof(STARTUPINFO);\r
9006   siStartInfo.lpReserved = NULL;\r
9007   siStartInfo.lpDesktop = NULL;\r
9008   siStartInfo.lpTitle = NULL;\r
9009   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9010   siStartInfo.cbReserved2 = 0;\r
9011   siStartInfo.lpReserved2 = NULL;\r
9012   siStartInfo.hStdInput = hChildStdinRd;\r
9013   siStartInfo.hStdOutput = hChildStdoutWr;\r
9014   siStartInfo.hStdError = hChildStdoutWr;\r
9015 \r
9016   fSuccess = CreateProcess(NULL,\r
9017                            cmdLine,        /* command line */\r
9018                            NULL,           /* process security attributes */\r
9019                            NULL,           /* primary thread security attrs */\r
9020                            TRUE,           /* handles are inherited */\r
9021                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9022                            NULL,           /* use parent's environment */\r
9023                            NULL,\r
9024                            &siStartInfo, /* STARTUPINFO pointer */\r
9025                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9026 \r
9027   err = GetLastError();\r
9028   SetCurrentDirectory(buf); /* return to prev directory */\r
9029   if (! fSuccess) {\r
9030     return err;\r
9031   }\r
9032 \r
9033   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9034     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9035     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9036   }\r
9037 \r
9038   /* Close the handles we don't need in the parent */\r
9039   CloseHandle(piProcInfo.hThread);\r
9040   CloseHandle(hChildStdinRd);\r
9041   CloseHandle(hChildStdoutWr);\r
9042 \r
9043   /* Prepare return value */\r
9044   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9045   cp->kind = CPReal;\r
9046   cp->hProcess = piProcInfo.hProcess;\r
9047   cp->pid = piProcInfo.dwProcessId;\r
9048   cp->hFrom = hChildStdoutRdDup;\r
9049   cp->hTo = hChildStdinWrDup;\r
9050 \r
9051   *pr = (void *) cp;\r
9052 \r
9053   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9054      2000 where engines sometimes don't see the initial command(s)\r
9055      from WinBoard and hang.  I don't understand how that can happen,\r
9056      but the Sleep is harmless, so I've put it in.  Others have also\r
9057      reported what may be the same problem, so hopefully this will fix\r
9058      it for them too.  */\r
9059   Sleep(500);\r
9060 \r
9061   return NO_ERROR;\r
9062 }\r
9063 \r
9064 \r
9065 void\r
9066 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9067 {\r
9068   ChildProc *cp; int result;\r
9069 \r
9070   cp = (ChildProc *) pr;\r
9071   if (cp == NULL) return;\r
9072 \r
9073   switch (cp->kind) {\r
9074   case CPReal:\r
9075     /* TerminateProcess is considered harmful, so... */\r
9076     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9077     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9078     /* The following doesn't work because the chess program\r
9079        doesn't "have the same console" as WinBoard.  Maybe\r
9080        we could arrange for this even though neither WinBoard\r
9081        nor the chess program uses a console for stdio? */\r
9082     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9083 \r
9084     /* [AS] Special termination modes for misbehaving programs... */\r
9085     if( signal == 9 ) { \r
9086         result = TerminateProcess( cp->hProcess, 0 );\r
9087 \r
9088         if ( appData.debugMode) {\r
9089             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9090         }\r
9091     }\r
9092     else if( signal == 10 ) {\r
9093         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9094 \r
9095         if( dw != WAIT_OBJECT_0 ) {\r
9096             result = TerminateProcess( cp->hProcess, 0 );\r
9097 \r
9098             if ( appData.debugMode) {\r
9099                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9100             }\r
9101 \r
9102         }\r
9103     }\r
9104 \r
9105     CloseHandle(cp->hProcess);\r
9106     break;\r
9107 \r
9108   case CPComm:\r
9109     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9110     break;\r
9111 \r
9112   case CPSock:\r
9113     closesocket(cp->sock);\r
9114     WSACleanup();\r
9115     break;\r
9116 \r
9117   case CPRcmd:\r
9118     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9119     closesocket(cp->sock);\r
9120     closesocket(cp->sock2);\r
9121     WSACleanup();\r
9122     break;\r
9123   }\r
9124   free(cp);\r
9125 }\r
9126 \r
9127 void\r
9128 InterruptChildProcess(ProcRef pr)\r
9129 {\r
9130   ChildProc *cp;\r
9131 \r
9132   cp = (ChildProc *) pr;\r
9133   if (cp == NULL) return;\r
9134   switch (cp->kind) {\r
9135   case CPReal:\r
9136     /* The following doesn't work because the chess program\r
9137        doesn't "have the same console" as WinBoard.  Maybe\r
9138        we could arrange for this even though neither WinBoard\r
9139        nor the chess program uses a console for stdio */\r
9140     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9141     break;\r
9142 \r
9143   case CPComm:\r
9144   case CPSock:\r
9145     /* Can't interrupt */\r
9146     break;\r
9147 \r
9148   case CPRcmd:\r
9149     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9150     break;\r
9151   }\r
9152 }\r
9153 \r
9154 \r
9155 int\r
9156 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9157 {\r
9158   char cmdLine[MSG_SIZ];\r
9159 \r
9160   if (port[0] == NULLCHAR) {\r
9161     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9162   } else {\r
9163     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9164   }\r
9165   return StartChildProcess(cmdLine, "", pr);\r
9166 }\r
9167 \r
9168 \r
9169 /* Code to open TCP sockets */\r
9170 \r
9171 int\r
9172 OpenTCP(char *host, char *port, ProcRef *pr)\r
9173 {\r
9174   ChildProc *cp;\r
9175   int err;\r
9176   SOCKET s;\r
9177   struct sockaddr_in sa, mysa;\r
9178   struct hostent FAR *hp;\r
9179   unsigned short uport;\r
9180   WORD wVersionRequested;\r
9181   WSADATA wsaData;\r
9182 \r
9183   /* Initialize socket DLL */\r
9184   wVersionRequested = MAKEWORD(1, 1);\r
9185   err = WSAStartup(wVersionRequested, &wsaData);\r
9186   if (err != 0) return err;\r
9187 \r
9188   /* Make socket */\r
9189   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9190     err = WSAGetLastError();\r
9191     WSACleanup();\r
9192     return err;\r
9193   }\r
9194 \r
9195   /* Bind local address using (mostly) don't-care values.\r
9196    */\r
9197   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9198   mysa.sin_family = AF_INET;\r
9199   mysa.sin_addr.s_addr = INADDR_ANY;\r
9200   uport = (unsigned short) 0;\r
9201   mysa.sin_port = htons(uport);\r
9202   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9203       == SOCKET_ERROR) {\r
9204     err = WSAGetLastError();\r
9205     WSACleanup();\r
9206     return err;\r
9207   }\r
9208 \r
9209   /* Resolve remote host name */\r
9210   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9211   if (!(hp = gethostbyname(host))) {\r
9212     unsigned int b0, b1, b2, b3;\r
9213 \r
9214     err = WSAGetLastError();\r
9215 \r
9216     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9217       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9218       hp->h_addrtype = AF_INET;\r
9219       hp->h_length = 4;\r
9220       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9221       hp->h_addr_list[0] = (char *) malloc(4);\r
9222       hp->h_addr_list[0][0] = (char) b0;\r
9223       hp->h_addr_list[0][1] = (char) b1;\r
9224       hp->h_addr_list[0][2] = (char) b2;\r
9225       hp->h_addr_list[0][3] = (char) b3;\r
9226     } else {\r
9227       WSACleanup();\r
9228       return err;\r
9229     }\r
9230   }\r
9231   sa.sin_family = hp->h_addrtype;\r
9232   uport = (unsigned short) atoi(port);\r
9233   sa.sin_port = htons(uport);\r
9234   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9235 \r
9236   /* Make connection */\r
9237   if (connect(s, (struct sockaddr *) &sa,\r
9238               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9239     err = WSAGetLastError();\r
9240     WSACleanup();\r
9241     return err;\r
9242   }\r
9243 \r
9244   /* Prepare return value */\r
9245   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9246   cp->kind = CPSock;\r
9247   cp->sock = s;\r
9248   *pr = (ProcRef *) cp;\r
9249 \r
9250   return NO_ERROR;\r
9251 }\r
9252 \r
9253 int\r
9254 OpenCommPort(char *name, ProcRef *pr)\r
9255 {\r
9256   HANDLE h;\r
9257   COMMTIMEOUTS ct;\r
9258   ChildProc *cp;\r
9259   char fullname[MSG_SIZ];\r
9260 \r
9261   if (*name != '\\')\r
9262     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9263   else\r
9264     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9265 \r
9266   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9267                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9268   if (h == (HANDLE) -1) {\r
9269     return GetLastError();\r
9270   }\r
9271   hCommPort = h;\r
9272 \r
9273   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9274 \r
9275   /* Accumulate characters until a 100ms pause, then parse */\r
9276   ct.ReadIntervalTimeout = 100;\r
9277   ct.ReadTotalTimeoutMultiplier = 0;\r
9278   ct.ReadTotalTimeoutConstant = 0;\r
9279   ct.WriteTotalTimeoutMultiplier = 0;\r
9280   ct.WriteTotalTimeoutConstant = 0;\r
9281   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9282 \r
9283   /* Prepare return value */\r
9284   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9285   cp->kind = CPComm;\r
9286   cp->hFrom = h;\r
9287   cp->hTo = h;\r
9288   *pr = (ProcRef *) cp;\r
9289 \r
9290   return NO_ERROR;\r
9291 }\r
9292 \r
9293 int\r
9294 OpenLoopback(ProcRef *pr)\r
9295 {\r
9296   DisplayFatalError(_("Not implemented"), 0, 1);\r
9297   return NO_ERROR;\r
9298 }\r
9299 \r
9300 \r
9301 int\r
9302 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9303 {\r
9304   ChildProc *cp;\r
9305   int err;\r
9306   SOCKET s, s2, s3;\r
9307   struct sockaddr_in sa, mysa;\r
9308   struct hostent FAR *hp;\r
9309   unsigned short uport;\r
9310   WORD wVersionRequested;\r
9311   WSADATA wsaData;\r
9312   int fromPort;\r
9313   char stderrPortStr[MSG_SIZ];\r
9314 \r
9315   /* Initialize socket DLL */\r
9316   wVersionRequested = MAKEWORD(1, 1);\r
9317   err = WSAStartup(wVersionRequested, &wsaData);\r
9318   if (err != 0) return err;\r
9319 \r
9320   /* Resolve remote host name */\r
9321   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9322   if (!(hp = gethostbyname(host))) {\r
9323     unsigned int b0, b1, b2, b3;\r
9324 \r
9325     err = WSAGetLastError();\r
9326 \r
9327     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9328       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9329       hp->h_addrtype = AF_INET;\r
9330       hp->h_length = 4;\r
9331       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9332       hp->h_addr_list[0] = (char *) malloc(4);\r
9333       hp->h_addr_list[0][0] = (char) b0;\r
9334       hp->h_addr_list[0][1] = (char) b1;\r
9335       hp->h_addr_list[0][2] = (char) b2;\r
9336       hp->h_addr_list[0][3] = (char) b3;\r
9337     } else {\r
9338       WSACleanup();\r
9339       return err;\r
9340     }\r
9341   }\r
9342   sa.sin_family = hp->h_addrtype;\r
9343   uport = (unsigned short) 514;\r
9344   sa.sin_port = htons(uport);\r
9345   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9346 \r
9347   /* Bind local socket to unused "privileged" port address\r
9348    */\r
9349   s = INVALID_SOCKET;\r
9350   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9351   mysa.sin_family = AF_INET;\r
9352   mysa.sin_addr.s_addr = INADDR_ANY;\r
9353   for (fromPort = 1023;; fromPort--) {\r
9354     if (fromPort < 0) {\r
9355       WSACleanup();\r
9356       return WSAEADDRINUSE;\r
9357     }\r
9358     if (s == INVALID_SOCKET) {\r
9359       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9360         err = WSAGetLastError();\r
9361         WSACleanup();\r
9362         return err;\r
9363       }\r
9364     }\r
9365     uport = (unsigned short) fromPort;\r
9366     mysa.sin_port = htons(uport);\r
9367     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9368         == SOCKET_ERROR) {\r
9369       err = WSAGetLastError();\r
9370       if (err == WSAEADDRINUSE) continue;\r
9371       WSACleanup();\r
9372       return err;\r
9373     }\r
9374     if (connect(s, (struct sockaddr *) &sa,\r
9375       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9376       err = WSAGetLastError();\r
9377       if (err == WSAEADDRINUSE) {\r
9378         closesocket(s);\r
9379         s = -1;\r
9380         continue;\r
9381       }\r
9382       WSACleanup();\r
9383       return err;\r
9384     }\r
9385     break;\r
9386   }\r
9387 \r
9388   /* Bind stderr local socket to unused "privileged" port address\r
9389    */\r
9390   s2 = INVALID_SOCKET;\r
9391   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9392   mysa.sin_family = AF_INET;\r
9393   mysa.sin_addr.s_addr = INADDR_ANY;\r
9394   for (fromPort = 1023;; fromPort--) {\r
9395     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9396     if (fromPort < 0) {\r
9397       (void) closesocket(s);\r
9398       WSACleanup();\r
9399       return WSAEADDRINUSE;\r
9400     }\r
9401     if (s2 == INVALID_SOCKET) {\r
9402       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9403         err = WSAGetLastError();\r
9404         closesocket(s);\r
9405         WSACleanup();\r
9406         return err;\r
9407       }\r
9408     }\r
9409     uport = (unsigned short) fromPort;\r
9410     mysa.sin_port = htons(uport);\r
9411     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9412         == SOCKET_ERROR) {\r
9413       err = WSAGetLastError();\r
9414       if (err == WSAEADDRINUSE) continue;\r
9415       (void) closesocket(s);\r
9416       WSACleanup();\r
9417       return err;\r
9418     }\r
9419     if (listen(s2, 1) == SOCKET_ERROR) {\r
9420       err = WSAGetLastError();\r
9421       if (err == WSAEADDRINUSE) {\r
9422         closesocket(s2);\r
9423         s2 = INVALID_SOCKET;\r
9424         continue;\r
9425       }\r
9426       (void) closesocket(s);\r
9427       (void) closesocket(s2);\r
9428       WSACleanup();\r
9429       return err;\r
9430     }\r
9431     break;\r
9432   }\r
9433   prevStderrPort = fromPort; // remember port used\r
9434   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9435 \r
9436   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9437     err = WSAGetLastError();\r
9438     (void) closesocket(s);\r
9439     (void) closesocket(s2);\r
9440     WSACleanup();\r
9441     return err;\r
9442   }\r
9443 \r
9444   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9445     err = WSAGetLastError();\r
9446     (void) closesocket(s);\r
9447     (void) closesocket(s2);\r
9448     WSACleanup();\r
9449     return err;\r
9450   }\r
9451   if (*user == NULLCHAR) user = UserName();\r
9452   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9453     err = WSAGetLastError();\r
9454     (void) closesocket(s);\r
9455     (void) closesocket(s2);\r
9456     WSACleanup();\r
9457     return err;\r
9458   }\r
9459   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9460     err = WSAGetLastError();\r
9461     (void) closesocket(s);\r
9462     (void) closesocket(s2);\r
9463     WSACleanup();\r
9464     return err;\r
9465   }\r
9466 \r
9467   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9468     err = WSAGetLastError();\r
9469     (void) closesocket(s);\r
9470     (void) closesocket(s2);\r
9471     WSACleanup();\r
9472     return err;\r
9473   }\r
9474   (void) closesocket(s2);  /* Stop listening */\r
9475 \r
9476   /* Prepare return value */\r
9477   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9478   cp->kind = CPRcmd;\r
9479   cp->sock = s;\r
9480   cp->sock2 = s3;\r
9481   *pr = (ProcRef *) cp;\r
9482 \r
9483   return NO_ERROR;\r
9484 }\r
9485 \r
9486 \r
9487 InputSourceRef\r
9488 AddInputSource(ProcRef pr, int lineByLine,\r
9489                InputCallback func, VOIDSTAR closure)\r
9490 {\r
9491   InputSource *is, *is2 = NULL;\r
9492   ChildProc *cp = (ChildProc *) pr;\r
9493 \r
9494   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9495   is->lineByLine = lineByLine;\r
9496   is->func = func;\r
9497   is->closure = closure;\r
9498   is->second = NULL;\r
9499   is->next = is->buf;\r
9500   if (pr == NoProc) {\r
9501     is->kind = CPReal;\r
9502     consoleInputSource = is;\r
9503   } else {\r
9504     is->kind = cp->kind;\r
9505     /* \r
9506         [AS] Try to avoid a race condition if the thread is given control too early:\r
9507         we create all threads suspended so that the is->hThread variable can be\r
9508         safely assigned, then let the threads start with ResumeThread.\r
9509     */\r
9510     switch (cp->kind) {\r
9511     case CPReal:\r
9512       is->hFile = cp->hFrom;\r
9513       cp->hFrom = NULL; /* now owned by InputThread */\r
9514       is->hThread =\r
9515         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9516                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9517       break;\r
9518 \r
9519     case CPComm:\r
9520       is->hFile = cp->hFrom;\r
9521       cp->hFrom = NULL; /* now owned by InputThread */\r
9522       is->hThread =\r
9523         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9524                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9525       break;\r
9526 \r
9527     case CPSock:\r
9528       is->sock = cp->sock;\r
9529       is->hThread =\r
9530         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9531                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9532       break;\r
9533 \r
9534     case CPRcmd:\r
9535       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9536       *is2 = *is;\r
9537       is->sock = cp->sock;\r
9538       is->second = is2;\r
9539       is2->sock = cp->sock2;\r
9540       is2->second = is2;\r
9541       is->hThread =\r
9542         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9543                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9544       is2->hThread =\r
9545         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9546                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9547       break;\r
9548     }\r
9549 \r
9550     if( is->hThread != NULL ) {\r
9551         ResumeThread( is->hThread );\r
9552     }\r
9553 \r
9554     if( is2 != NULL && is2->hThread != NULL ) {\r
9555         ResumeThread( is2->hThread );\r
9556     }\r
9557   }\r
9558 \r
9559   return (InputSourceRef) is;\r
9560 }\r
9561 \r
9562 void\r
9563 RemoveInputSource(InputSourceRef isr)\r
9564 {\r
9565   InputSource *is;\r
9566 \r
9567   is = (InputSource *) isr;\r
9568   is->hThread = NULL;  /* tell thread to stop */\r
9569   CloseHandle(is->hThread);\r
9570   if (is->second != NULL) {\r
9571     is->second->hThread = NULL;\r
9572     CloseHandle(is->second->hThread);\r
9573   }\r
9574 }\r
9575 \r
9576 int no_wrap(char *message, int count)\r
9577 {\r
9578     ConsoleOutput(message, count, FALSE);\r
9579     return count;\r
9580 }\r
9581 \r
9582 int\r
9583 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9584 {\r
9585   DWORD dOutCount;\r
9586   int outCount = SOCKET_ERROR;\r
9587   ChildProc *cp = (ChildProc *) pr;\r
9588   static OVERLAPPED ovl;\r
9589   static int line = 0;\r
9590 \r
9591   if (pr == NoProc)\r
9592   {\r
9593     if (appData.noJoin || !appData.useInternalWrap)\r
9594       return no_wrap(message, count);\r
9595     else\r
9596     {\r
9597       int width = get_term_width();\r
9598       int len = wrap(NULL, message, count, width, &line);\r
9599       char *msg = malloc(len);\r
9600       int dbgchk;\r
9601 \r
9602       if (!msg)\r
9603         return no_wrap(message, count);\r
9604       else\r
9605       {\r
9606         dbgchk = wrap(msg, message, count, width, &line);\r
9607         if (dbgchk != len && appData.debugMode)\r
9608             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9609         ConsoleOutput(msg, len, FALSE);\r
9610         free(msg);\r
9611         return len;\r
9612       }\r
9613     }\r
9614   }\r
9615 \r
9616   if (ovl.hEvent == NULL) {\r
9617     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9618   }\r
9619   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9620 \r
9621   switch (cp->kind) {\r
9622   case CPSock:\r
9623   case CPRcmd:\r
9624     outCount = send(cp->sock, message, count, 0);\r
9625     if (outCount == SOCKET_ERROR) {\r
9626       *outError = WSAGetLastError();\r
9627     } else {\r
9628       *outError = NO_ERROR;\r
9629     }\r
9630     break;\r
9631 \r
9632   case CPReal:\r
9633     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9634                   &dOutCount, NULL)) {\r
9635       *outError = NO_ERROR;\r
9636       outCount = (int) dOutCount;\r
9637     } else {\r
9638       *outError = GetLastError();\r
9639     }\r
9640     break;\r
9641 \r
9642   case CPComm:\r
9643     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9644                             &dOutCount, &ovl);\r
9645     if (*outError == NO_ERROR) {\r
9646       outCount = (int) dOutCount;\r
9647     }\r
9648     break;\r
9649   }\r
9650   return outCount;\r
9651 }\r
9652 \r
9653 void\r
9654 DoSleep(int n)\r
9655 {\r
9656     if(n != 0) Sleep(n);\r
9657 }\r
9658 \r
9659 int\r
9660 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9661                        long msdelay)\r
9662 {\r
9663   /* Ignore delay, not implemented for WinBoard */\r
9664   return OutputToProcess(pr, message, count, outError);\r
9665 }\r
9666 \r
9667 \r
9668 void\r
9669 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9670                         char *buf, int count, int error)\r
9671 {\r
9672   DisplayFatalError(_("Not implemented"), 0, 1);\r
9673 }\r
9674 \r
9675 /* see wgamelist.c for Game List functions */\r
9676 /* see wedittags.c for Edit Tags functions */\r
9677 \r
9678 \r
9679 VOID\r
9680 ICSInitScript()\r
9681 {\r
9682   FILE *f;\r
9683   char buf[MSG_SIZ];\r
9684   char *dummy;\r
9685 \r
9686   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9687     f = fopen(buf, "r");\r
9688     if (f != NULL) {\r
9689       ProcessICSInitScript(f);\r
9690       fclose(f);\r
9691     }\r
9692   }\r
9693 }\r
9694 \r
9695 \r
9696 VOID\r
9697 StartAnalysisClock()\r
9698 {\r
9699   if (analysisTimerEvent) return;\r
9700   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9701                                         (UINT) 2000, NULL);\r
9702 }\r
9703 \r
9704 VOID\r
9705 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9706 {\r
9707   highlightInfo.sq[0].x = fromX;\r
9708   highlightInfo.sq[0].y = fromY;\r
9709   highlightInfo.sq[1].x = toX;\r
9710   highlightInfo.sq[1].y = toY;\r
9711 }\r
9712 \r
9713 VOID\r
9714 ClearHighlights()\r
9715 {\r
9716   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9717     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9718 }\r
9719 \r
9720 VOID\r
9721 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9722 {\r
9723   premoveHighlightInfo.sq[0].x = fromX;\r
9724   premoveHighlightInfo.sq[0].y = fromY;\r
9725   premoveHighlightInfo.sq[1].x = toX;\r
9726   premoveHighlightInfo.sq[1].y = toY;\r
9727 }\r
9728 \r
9729 VOID\r
9730 ClearPremoveHighlights()\r
9731 {\r
9732   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9733     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9734 }\r
9735 \r
9736 VOID\r
9737 ShutDownFrontEnd()\r
9738 {\r
9739   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9740   DeleteClipboardTempFiles();\r
9741 }\r
9742 \r
9743 void\r
9744 BoardToTop()\r
9745 {\r
9746     if (IsIconic(hwndMain))\r
9747       ShowWindow(hwndMain, SW_RESTORE);\r
9748 \r
9749     SetActiveWindow(hwndMain);\r
9750 }\r
9751 \r
9752 /*\r
9753  * Prototypes for animation support routines\r
9754  */\r
9755 static void ScreenSquare(int column, int row, POINT * pt);\r
9756 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9757      POINT frames[], int * nFrames);\r
9758 \r
9759 \r
9760 #define kFactor 4\r
9761 \r
9762 void\r
9763 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9764 {       // [HGM] atomic: animate blast wave\r
9765         int i;\r
9766 \r
9767         explodeInfo.fromX = fromX;\r
9768         explodeInfo.fromY = fromY;\r
9769         explodeInfo.toX = toX;\r
9770         explodeInfo.toY = toY;\r
9771         for(i=1; i<4*kFactor; i++) {\r
9772             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9773             DrawPosition(FALSE, board);\r
9774             Sleep(appData.animSpeed);\r
9775         }\r
9776         explodeInfo.radius = 0;\r
9777         DrawPosition(TRUE, board);\r
9778 }\r
9779 \r
9780 void\r
9781 AnimateMove(board, fromX, fromY, toX, toY)\r
9782      Board board;\r
9783      int fromX;\r
9784      int fromY;\r
9785      int toX;\r
9786      int toY;\r
9787 {\r
9788   ChessSquare piece;\r
9789   POINT start, finish, mid;\r
9790   POINT frames[kFactor * 2 + 1];\r
9791   int nFrames, n;\r
9792 \r
9793   if (!appData.animate) return;\r
9794   if (doingSizing) return;\r
9795   if (fromY < 0 || fromX < 0) return;\r
9796   piece = board[fromY][fromX];\r
9797   if (piece >= EmptySquare) return;\r
9798 \r
9799   ScreenSquare(fromX, fromY, &start);\r
9800   ScreenSquare(toX, toY, &finish);\r
9801 \r
9802   /* All moves except knight jumps move in straight line */\r
9803   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9804     mid.x = start.x + (finish.x - start.x) / 2;\r
9805     mid.y = start.y + (finish.y - start.y) / 2;\r
9806   } else {\r
9807     /* Knight: make straight movement then diagonal */\r
9808     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9809        mid.x = start.x + (finish.x - start.x) / 2;\r
9810        mid.y = start.y;\r
9811      } else {\r
9812        mid.x = start.x;\r
9813        mid.y = start.y + (finish.y - start.y) / 2;\r
9814      }\r
9815   }\r
9816   \r
9817   /* Don't use as many frames for very short moves */\r
9818   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9819     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9820   else\r
9821     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9822 \r
9823   animInfo.from.x = fromX;\r
9824   animInfo.from.y = fromY;\r
9825   animInfo.to.x = toX;\r
9826   animInfo.to.y = toY;\r
9827   animInfo.lastpos = start;\r
9828   animInfo.piece = piece;\r
9829   for (n = 0; n < nFrames; n++) {\r
9830     animInfo.pos = frames[n];\r
9831     DrawPosition(FALSE, NULL);\r
9832     animInfo.lastpos = animInfo.pos;\r
9833     Sleep(appData.animSpeed);\r
9834   }\r
9835   animInfo.pos = finish;\r
9836   DrawPosition(FALSE, NULL);\r
9837   animInfo.piece = EmptySquare;\r
9838   Explode(board, fromX, fromY, toX, toY);\r
9839 }\r
9840 \r
9841 /*      Convert board position to corner of screen rect and color       */\r
9842 \r
9843 static void\r
9844 ScreenSquare(column, row, pt)\r
9845      int column; int row; POINT * pt;\r
9846 {\r
9847   if (flipView) {\r
9848     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9849     pt->y = lineGap + row * (squareSize + lineGap);\r
9850   } else {\r
9851     pt->x = lineGap + column * (squareSize + lineGap);\r
9852     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9853   }\r
9854 }\r
9855 \r
9856 /*      Generate a series of frame coords from start->mid->finish.\r
9857         The movement rate doubles until the half way point is\r
9858         reached, then halves back down to the final destination,\r
9859         which gives a nice slow in/out effect. The algorithmn\r
9860         may seem to generate too many intermediates for short\r
9861         moves, but remember that the purpose is to attract the\r
9862         viewers attention to the piece about to be moved and\r
9863         then to where it ends up. Too few frames would be less\r
9864         noticeable.                                             */\r
9865 \r
9866 static void\r
9867 Tween(start, mid, finish, factor, frames, nFrames)\r
9868      POINT * start; POINT * mid;\r
9869      POINT * finish; int factor;\r
9870      POINT frames[]; int * nFrames;\r
9871 {\r
9872   int n, fraction = 1, count = 0;\r
9873 \r
9874   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9875   for (n = 0; n < factor; n++)\r
9876     fraction *= 2;\r
9877   for (n = 0; n < factor; n++) {\r
9878     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9879     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9880     count ++;\r
9881     fraction = fraction / 2;\r
9882   }\r
9883   \r
9884   /* Midpoint */\r
9885   frames[count] = *mid;\r
9886   count ++;\r
9887   \r
9888   /* Slow out, stepping 1/2, then 1/4, ... */\r
9889   fraction = 2;\r
9890   for (n = 0; n < factor; n++) {\r
9891     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9892     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9893     count ++;\r
9894     fraction = fraction * 2;\r
9895   }\r
9896   *nFrames = count;\r
9897 }\r
9898 \r
9899 void\r
9900 SettingsPopUp(ChessProgramState *cps)\r
9901 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9902       EngineOptionsPopup(savedHwnd, cps);\r
9903 }\r
9904 \r
9905 int flock(int fid, int code)\r
9906 {\r
9907     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9908     OVERLAPPED ov;\r
9909     ov.hEvent = NULL;\r
9910     ov.Offset = 0;\r
9911     ov.OffsetHigh = 0;\r
9912     switch(code) {\r
9913       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9914       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9915       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9916       default: return -1;\r
9917     }\r
9918     return 0;\r
9919 }\r