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