Add -useBoardTexture and -usePieceFont options
[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, IDOK, IDCANCEL },\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
258   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
261   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
264   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
265 { ABOUTBOX2, IDC_ChessBoard }, \r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
267   OPT_GameListClose, IDC_GameListDoFilter }, \r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
269 { DLG_Error, IDOK }, \r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
271   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
274   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
275   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
276 { DLG_IndexNumber, IDC_Index }, \r
277 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
278 { DLG_TypeInName, IDOK, IDCANCEL }, \r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
280   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
282   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
283   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
284   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
285   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
286   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
287   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
289   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
290   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
291   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
292   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
293   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
294   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
295   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
296   GPB_General, GPB_Alarm }, \r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
298   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
299   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
300   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
301   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
302   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
303   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
304   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont }, \r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
306   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
307   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
308   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
309   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
310   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
311   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
312   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
313   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
315   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
316   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
317   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
318   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
321   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
322 { DLG_MoveHistory }, \r
323 { DLG_EvalGraph }, \r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
327   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
328   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
329   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
331   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
332   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
333 { 0 }\r
334 };\r
335 \r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
337 static int lastChecked;\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
339 extern int tinyLayout;\r
340 extern char * menuBarText[][10];\r
341 \r
342 void\r
343 LoadLanguageFile(char *name)\r
344 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
345     FILE *f;\r
346     int i=0, j=0, n=0, k;\r
347     char buf[MSG_SIZ];\r
348 \r
349     if(!name || name[0] == NULLCHAR) return;\r
350       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
351     appData.language = oldLanguage;\r
352     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
353     if((f = fopen(buf, "r")) == NULL) return;\r
354     while((k = fgetc(f)) != EOF) {\r
355         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
356         languageBuf[i] = k;\r
357         if(k == '\n') {\r
358             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
359                 char *p;\r
360                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
361                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
362                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
363                         english[j] = languageBuf + n + 1; *p = 0;\r
364                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
366                     }\r
367                 }\r
368             }\r
369             n = i + 1;\r
370         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
371             switch(k) {\r
372               case 'n': k = '\n'; break;\r
373               case 'r': k = '\r'; break;\r
374               case 't': k = '\t'; break;\r
375             }\r
376             languageBuf[--i] = k;\r
377         }\r
378         i++;\r
379     }\r
380     fclose(f);\r
381     barbaric = (j != 0);\r
382     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
383 }\r
384 \r
385 char *\r
386 T_(char *s)\r
387 {   // return the translation of the given string\r
388     // efficiency can be improved a lot...\r
389     int i=0;\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
391     if(!barbaric) return s;\r
392     if(!s) return ""; // sanity\r
393     while(english[i]) {\r
394         if(!strcmp(s, english[i])) return foreign[i];\r
395         i++;\r
396     }\r
397     return s;\r
398 }\r
399 \r
400 void\r
401 Translate(HWND hDlg, int dialogID)\r
402 {   // translate all text items in the given dialog\r
403     int i=0, j, k;\r
404     char buf[MSG_SIZ], *s;\r
405     if(!barbaric) return;\r
406     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
407     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
408     GetWindowText( hDlg, buf, MSG_SIZ );\r
409     s = T_(buf);\r
410     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
411     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
412         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
413         if(strlen(buf) == 0) continue;\r
414         s = T_(buf);\r
415         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
416     }\r
417 }\r
418 \r
419 HMENU\r
420 TranslateOneMenu(int i, HMENU subMenu)\r
421 {\r
422     int j;\r
423     static MENUITEMINFO info;\r
424 \r
425     info.cbSize = sizeof(MENUITEMINFO);\r
426     info.fMask = MIIM_STATE | MIIM_TYPE;\r
427           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
428             char buf[MSG_SIZ];\r
429             info.dwTypeData = buf;\r
430             info.cch = sizeof(buf);\r
431             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
432             if(i < 10) {
433                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
434                 else menuText[i][j] = strdup(buf); // remember original on first change\r
435             }\r
436             if(buf[0] == NULLCHAR) continue;\r
437             info.dwTypeData = T_(buf);\r
438             info.cch = strlen(buf)+1;\r
439             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
440           }\r
441     return subMenu;\r
442 }\r
443 \r
444 void\r
445 TranslateMenus(int addLanguage)\r
446 {\r
447     int i;\r
448     WIN32_FIND_DATA fileData;\r
449     HANDLE hFind;\r
450 #define IDM_English 1970\r
451     if(1) {\r
452         HMENU mainMenu = GetMenu(hwndMain);\r
453         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
454           HMENU subMenu = GetSubMenu(mainMenu, i);\r
455           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
456                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
457           TranslateOneMenu(i, subMenu);\r
458         }\r
459         DrawMenuBar(hwndMain);\r
460     }\r
461 \r
462     if(!addLanguage) return;\r
463     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
464         HMENU mainMenu = GetMenu(hwndMain);\r
465         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
466         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
467         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
468         i = 0; lastChecked = IDM_English;\r
469         do {\r
470             char *p, *q = fileData.cFileName;\r
471             int checkFlag = MF_UNCHECKED;\r
472             languageFile[i] = strdup(q);\r
473             if(barbaric && !strcmp(oldLanguage, q)) {\r
474                 checkFlag = MF_CHECKED;\r
475                 lastChecked = IDM_English + i + 1;\r
476                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
477             }\r
478             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
479             p = strstr(fileData.cFileName, ".lng");\r
480             if(p) *p = 0;\r
481             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
482         } while(FindNextFile(hFind, &fileData));\r
483         FindClose(hFind);\r
484     }\r
485 }\r
486 \r
487 #endif\r
488 \r
489 typedef struct {\r
490   char *name;\r
491   int squareSize;\r
492   int lineGap;\r
493   int smallLayout;\r
494   int tinyLayout;\r
495   int cliWidth, cliHeight;\r
496 } SizeInfo;\r
497 \r
498 SizeInfo sizeInfo[] = \r
499 {\r
500   { "tiny",     21, 0, 1, 1, 0, 0 },\r
501   { "teeny",    25, 1, 1, 1, 0, 0 },\r
502   { "dinky",    29, 1, 1, 1, 0, 0 },\r
503   { "petite",   33, 1, 1, 1, 0, 0 },\r
504   { "slim",     37, 2, 1, 0, 0, 0 },\r
505   { "small",    40, 2, 1, 0, 0, 0 },\r
506   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
507   { "middling", 49, 2, 0, 0, 0, 0 },\r
508   { "average",  54, 2, 0, 0, 0, 0 },\r
509   { "moderate", 58, 3, 0, 0, 0, 0 },\r
510   { "medium",   64, 3, 0, 0, 0, 0 },\r
511   { "bulky",    72, 3, 0, 0, 0, 0 },\r
512   { "large",    80, 3, 0, 0, 0, 0 },\r
513   { "big",      87, 3, 0, 0, 0, 0 },\r
514   { "huge",     95, 3, 0, 0, 0, 0 },\r
515   { "giant",    108, 3, 0, 0, 0, 0 },\r
516   { "colossal", 116, 4, 0, 0, 0, 0 },\r
517   { "titanic",  129, 4, 0, 0, 0, 0 },\r
518   { NULL, 0, 0, 0, 0, 0, 0 }\r
519 };\r
520 \r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
523 {\r
524   { 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
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539   { 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
540   { 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
541   { 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
542 };\r
543 \r
544 MyFont *font[NUM_SIZES][NUM_FONTS];\r
545 \r
546 typedef struct {\r
547   char *label;\r
548   int id;\r
549   HWND hwnd;\r
550   WNDPROC wndproc;\r
551 } MyButtonDesc;\r
552 \r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
554 #define N_BUTTONS 5\r
555 \r
556 MyButtonDesc buttonDesc[N_BUTTONS] =\r
557 {\r
558   {"<<", IDM_ToStart, NULL, NULL},\r
559   {"<", IDM_Backward, NULL, NULL},\r
560   {"P", IDM_Pause, NULL, NULL},\r
561   {">", IDM_Forward, NULL, NULL},\r
562   {">>", IDM_ToEnd, NULL, NULL},\r
563 };\r
564 \r
565 int tinyLayout = 0, smallLayout = 0;\r
566 #define MENU_BAR_ITEMS 9\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
568   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
569   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
570 };\r
571 \r
572 \r
573 MySound sounds[(int)NSoundClasses];\r
574 MyTextAttribs textAttribs[(int)NColorClasses];\r
575 \r
576 MyColorizeAttribs colorizeAttribs[] = {\r
577   { (COLORREF)0, 0, N_("Shout Text") },\r
578   { (COLORREF)0, 0, N_("SShout/CShout") },\r
579   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
580   { (COLORREF)0, 0, N_("Channel Text") },\r
581   { (COLORREF)0, 0, N_("Kibitz Text") },\r
582   { (COLORREF)0, 0, N_("Tell Text") },\r
583   { (COLORREF)0, 0, N_("Challenge Text") },\r
584   { (COLORREF)0, 0, N_("Request Text") },\r
585   { (COLORREF)0, 0, N_("Seek Text") },\r
586   { (COLORREF)0, 0, N_("Normal Text") },\r
587   { (COLORREF)0, 0, N_("None") }\r
588 };\r
589 \r
590 \r
591 \r
592 static char *commentTitle;\r
593 static char *commentText;\r
594 static int commentIndex;\r
595 static Boolean editComment = FALSE;\r
596 \r
597 \r
598 char errorTitle[MSG_SIZ];\r
599 char errorMessage[2*MSG_SIZ];\r
600 HWND errorDialog = NULL;\r
601 BOOLEAN moveErrorMessageUp = FALSE;\r
602 BOOLEAN consoleEcho = TRUE;\r
603 CHARFORMAT consoleCF;\r
604 COLORREF consoleBackgroundColor;\r
605 \r
606 char *programVersion;\r
607 \r
608 #define CPReal 1\r
609 #define CPComm 2\r
610 #define CPSock 3\r
611 #define CPRcmd 4\r
612 typedef int CPKind;\r
613 \r
614 typedef struct {\r
615   CPKind kind;\r
616   HANDLE hProcess;\r
617   DWORD pid;\r
618   HANDLE hTo;\r
619   HANDLE hFrom;\r
620   SOCKET sock;\r
621   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
622 } ChildProc;\r
623 \r
624 #define INPUT_SOURCE_BUF_SIZE 4096\r
625 \r
626 typedef struct _InputSource {\r
627   CPKind kind;\r
628   HANDLE hFile;\r
629   SOCKET sock;\r
630   int lineByLine;\r
631   HANDLE hThread;\r
632   DWORD id;\r
633   char buf[INPUT_SOURCE_BUF_SIZE];\r
634   char *next;\r
635   DWORD count;\r
636   int error;\r
637   InputCallback func;\r
638   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
639   VOIDSTAR closure;\r
640 } InputSource;\r
641 \r
642 InputSource *consoleInputSource;\r
643 \r
644 DCB dcb;\r
645 \r
646 /* forward */\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
648 VOID ConsoleCreate();\r
649 LRESULT CALLBACK\r
650   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);\r
654 LRESULT CALLBACK\r
655   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
657 void ParseIcsTextMenu(char *icsTextMenuString);\r
658 VOID PopUpNameDialog(char firstchar);\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
660 \r
661 /* [AS] */\r
662 int NewGameFRC();\r
663 int GameListOptions();\r
664 \r
665 int dummy; // [HGM] for obsolete args\r
666 \r
667 HWND hwndMain = NULL;        /* root window*/\r
668 HWND hwndConsole = NULL;\r
669 HWND commentDialog = NULL;\r
670 HWND moveHistoryDialog = NULL;\r
671 HWND evalGraphDialog = NULL;\r
672 HWND engineOutputDialog = NULL;\r
673 HWND gameListDialog = NULL;\r
674 HWND editTagsDialog = NULL;\r
675 \r
676 int commentUp = FALSE;\r
677 \r
678 WindowPlacement wpMain;\r
679 WindowPlacement wpConsole;\r
680 WindowPlacement wpComment;\r
681 WindowPlacement wpMoveHistory;\r
682 WindowPlacement wpEvalGraph;\r
683 WindowPlacement wpEngineOutput;\r
684 WindowPlacement wpGameList;\r
685 WindowPlacement wpTags;\r
686 \r
687 VOID EngineOptionsPopup(); // [HGM] settings\r
688 \r
689 VOID GothicPopUp(char *title, VariantClass variant);\r
690 /*\r
691  * Setting "frozen" should disable all user input other than deleting\r
692  * the window.  We do this while engines are initializing themselves.\r
693  */\r
694 static int frozen = 0;\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];\r
696 void FreezeUI()\r
697 {\r
698   HMENU hmenu;\r
699   int i;\r
700 \r
701   if (frozen) return;\r
702   frozen = 1;\r
703   hmenu = GetMenu(hwndMain);\r
704   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
705     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
706   }\r
707   DrawMenuBar(hwndMain);\r
708 }\r
709 \r
710 /* Undo a FreezeUI */\r
711 void ThawUI()\r
712 {\r
713   HMENU hmenu;\r
714   int i;\r
715 \r
716   if (!frozen) return;\r
717   frozen = 0;\r
718   hmenu = GetMenu(hwndMain);\r
719   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
720     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
721   }\r
722   DrawMenuBar(hwndMain);\r
723 }\r
724 \r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
726 \r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
728 #ifdef JAWS\r
729 #include "jaws.c"\r
730 #else\r
731 #define JAWS_INIT\r
732 #define JAWS_ARGS\r
733 #define JAWS_ALT_INTERCEPT\r
734 #define JAWS_KB_NAVIGATION\r
735 #define JAWS_MENU_ITEMS\r
736 #define JAWS_SILENCE\r
737 #define JAWS_REPLAY\r
738 #define JAWS_ACCEL\r
739 #define JAWS_COPYRIGHT\r
740 #define JAWS_DELETE(X) X\r
741 #define SAYMACHINEMOVE()\r
742 #define SAY(X)\r
743 #endif\r
744 \r
745 /*---------------------------------------------------------------------------*\\r
746  *\r
747  * WinMain\r
748  *\r
749 \*---------------------------------------------------------------------------*/\r
750 \r
751 int APIENTRY\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
753         LPSTR lpCmdLine, int nCmdShow)\r
754 {\r
755   MSG msg;\r
756   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
757 //  INITCOMMONCONTROLSEX ex;\r
758 \r
759   debugFP = stderr;\r
760 \r
761   LoadLibrary("RICHED32.DLL");\r
762   consoleCF.cbSize = sizeof(CHARFORMAT);\r
763 \r
764   if (!InitApplication(hInstance)) {\r
765     return (FALSE);\r
766   }\r
767   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
768     return (FALSE);\r
769   }\r
770 \r
771   JAWS_INIT\r
772   TranslateMenus(1);\r
773 \r
774 //  InitCommonControlsEx(&ex);\r
775   InitCommonControls();\r
776 \r
777   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
778   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
779   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
780 \r
781   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
782 \r
783   while (GetMessage(&msg, /* message structure */\r
784                     NULL, /* handle of window receiving the message */\r
785                     0,    /* lowest message to examine */\r
786                     0))   /* highest message to examine */\r
787     {\r
788 \r
789       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
790         // [HGM] navigate: switch between all windows with tab\r
791         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
792         int i, currentElement = 0;\r
793 \r
794         // first determine what element of the chain we come from (if any)\r
795         if(appData.icsActive) {\r
796             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
797             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
798         }\r
799         if(engineOutputDialog && EngineOutputIsUp()) {\r
800             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
801             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
802         }\r
803         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
804             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
805         }\r
806         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
807         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
808         if(msg.hwnd == e1)                 currentElement = 2; else\r
809         if(msg.hwnd == e2)                 currentElement = 3; else\r
810         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
811         if(msg.hwnd == mh)                currentElement = 4; else\r
812         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
813         if(msg.hwnd == hText)  currentElement = 5; else\r
814         if(msg.hwnd == hInput) currentElement = 6; else\r
815         for (i = 0; i < N_BUTTONS; i++) {\r
816             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
817         }\r
818 \r
819         // determine where to go to\r
820         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
821           do {\r
822             currentElement = (currentElement + direction) % 7;\r
823             switch(currentElement) {\r
824                 case 0:\r
825                   h = hwndMain; break; // passing this case always makes the loop exit\r
826                 case 1:\r
827                   h = buttonDesc[0].hwnd; break; // could be NULL\r
828                 case 2:\r
829                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
830                   h = e1; break;\r
831                 case 3:\r
832                   if(!EngineOutputIsUp()) continue;\r
833                   h = e2; break;\r
834                 case 4:\r
835                   if(!MoveHistoryIsUp()) continue;\r
836                   h = mh; break;\r
837 //              case 6: // input to eval graph does not seem to get here!\r
838 //                if(!EvalGraphIsUp()) continue;\r
839 //                h = evalGraphDialog; break;\r
840                 case 5:\r
841                   if(!appData.icsActive) continue;\r
842                   SAY("display");\r
843                   h = hText; break;\r
844                 case 6:\r
845                   if(!appData.icsActive) continue;\r
846                   SAY("input");\r
847                   h = hInput; break;\r
848             }\r
849           } while(h == 0);\r
850 \r
851           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
852           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
853           SetFocus(h);\r
854 \r
855           continue; // this message now has been processed\r
856         }\r
857       }\r
858 \r
859       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
860           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
861           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
862           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
863           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
864           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
865           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
866           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
868           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
869         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
870         for(i=0; i<MAX_CHAT; i++) \r
871             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
872                 done = 1; break;\r
873         }\r
874         if(done) continue; // [HGM] chat: end patch\r
875         TranslateMessage(&msg); /* Translates virtual key codes */\r
876         DispatchMessage(&msg);  /* Dispatches message to window */\r
877       }\r
878     }\r
879 \r
880 \r
881   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
882 }\r
883 \r
884 /*---------------------------------------------------------------------------*\\r
885  *\r
886  * Initialization functions\r
887  *\r
888 \*---------------------------------------------------------------------------*/\r
889 \r
890 void\r
891 SetUserLogo()\r
892 {   // update user logo if necessary\r
893     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
894 \r
895     if(appData.autoLogo) {\r
896           curName = UserName();\r
897           if(strcmp(curName, oldUserName)) {\r
898                 GetCurrentDirectory(MSG_SIZ, dir);\r
899                 SetCurrentDirectory(installDir);\r
900                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
901                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
902                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
903                 if(userLogo == NULL)\r
904                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
905                 SetCurrentDirectory(dir); /* return to prev directory */\r
906           }\r
907     }\r
908 }\r
909 \r
910 BOOL\r
911 InitApplication(HINSTANCE hInstance)\r
912 {\r
913   WNDCLASS wc;\r
914 \r
915   /* Fill in window class structure with parameters that describe the */\r
916   /* main window. */\r
917 \r
918   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
919   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
920   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
921   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
922   wc.hInstance     = hInstance;         /* Owner of this class */\r
923   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
924   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
925   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
926   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
927   wc.lpszClassName = szAppName;                 /* Name to register as */\r
928 \r
929   /* Register the window class and return success/failure code. */\r
930   if (!RegisterClass(&wc)) return FALSE;\r
931 \r
932   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
933   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
934   wc.cbClsExtra    = 0;\r
935   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
936   wc.hInstance     = hInstance;\r
937   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
938   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
939   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
940   wc.lpszMenuName  = NULL;\r
941   wc.lpszClassName = szConsoleName;\r
942 \r
943   if (!RegisterClass(&wc)) return FALSE;\r
944   return TRUE;\r
945 }\r
946 \r
947 \r
948 /* Set by InitInstance, used by EnsureOnScreen */\r
949 int screenHeight, screenWidth;\r
950 \r
951 void\r
952 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
953 {\r
954 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
955   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
956   if (*x > screenWidth - 32) *x = 0;\r
957   if (*y > screenHeight - 32) *y = 0;\r
958   if (*x < minX) *x = minX;\r
959   if (*y < minY) *y = minY;\r
960 }\r
961 \r
962 VOID\r
963 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
964 {\r
965   char buf[MSG_SIZ], dir[MSG_SIZ];\r
966   GetCurrentDirectory(MSG_SIZ, dir);\r
967   SetCurrentDirectory(installDir);\r
968   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
969       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
970 \r
971       if (cps->programLogo == NULL && appData.debugMode) {\r
972           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
973       }\r
974   } else if(appData.autoLogo) {\r
975       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
976         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
977         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
978       } else\r
979       if(appData.directory[n] && appData.directory[n][0]) {\r
980         SetCurrentDirectory(appData.directory[n]);\r
981         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
982       }\r
983   }\r
984   SetCurrentDirectory(dir); /* return to prev directory */\r
985 }\r
986 \r
987 VOID\r
988 InitTextures()\r
989 {\r
990   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
991   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
992   \r
993   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
994       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
995       liteBackTextureMode = appData.liteBackTextureMode;\r
996 \r
997       if (liteBackTexture == NULL && appData.debugMode) {\r
998           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
999       }\r
1000   }\r
1001   \r
1002   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1003       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1004       darkBackTextureMode = appData.darkBackTextureMode;\r
1005 \r
1006       if (darkBackTexture == NULL && appData.debugMode) {\r
1007           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1008       }\r
1009   }\r
1010 }\r
1011 \r
1012 BOOL\r
1013 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1014 {\r
1015   HWND hwnd; /* Main window handle. */\r
1016   int ibs;\r
1017   WINDOWPLACEMENT wp;\r
1018   char *filepart;\r
1019 \r
1020   hInst = hInstance;    /* Store instance handle in our global variable */\r
1021   programName = szAppName;\r
1022 \r
1023   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1024     *filepart = NULLCHAR;\r
1025   } else {\r
1026     GetCurrentDirectory(MSG_SIZ, installDir);\r
1027   }\r
1028   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1029   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1030   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1031   /* xboard, and older WinBoards, controlled the move sound with the\r
1032      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1033      always turn the option on (so that the backend will call us),\r
1034      then let the user turn the sound off by setting it to silence if\r
1035      desired.  To accommodate old winboard.ini files saved by old\r
1036      versions of WinBoard, we also turn off the sound if the option\r
1037      was initially set to false. [HGM] taken out of InitAppData */\r
1038   if (!appData.ringBellAfterMoves) {\r
1039     sounds[(int)SoundMove].name = strdup("");\r
1040     appData.ringBellAfterMoves = TRUE;\r
1041   }\r
1042   if (appData.debugMode) {\r
1043     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1044     setbuf(debugFP, NULL);\r
1045   }\r
1046 \r
1047   LoadLanguageFile(appData.language);\r
1048 \r
1049   InitBackEnd1();\r
1050 \r
1051 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1052 //  InitEngineUCI( installDir, &second );\r
1053 \r
1054   /* Create a main window for this application instance. */\r
1055   hwnd = CreateWindow(szAppName, szTitle,\r
1056                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1057                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1058                       NULL, NULL, hInstance, NULL);\r
1059   hwndMain = hwnd;\r
1060 \r
1061   /* If window could not be created, return "failure" */\r
1062   if (!hwnd) {\r
1063     return (FALSE);\r
1064   }\r
1065 \r
1066   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1067   LoadLogo(&first, 0, FALSE);\r
1068   LoadLogo(&second, 1, appData.icsActive);\r
1069 \r
1070   SetUserLogo();\r
1071 \r
1072   iconWhite = LoadIcon(hInstance, "icon_white");\r
1073   iconBlack = LoadIcon(hInstance, "icon_black");\r
1074   iconCurrent = iconWhite;\r
1075   InitDrawingColors();\r
1076   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1077   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1078   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1079     /* Compute window size for each board size, and use the largest\r
1080        size that fits on this screen as the default. */\r
1081     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1082     if (boardSize == (BoardSize)-1 &&\r
1083         winH <= screenHeight\r
1084            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1085         && winW <= screenWidth) {\r
1086       boardSize = (BoardSize)ibs;\r
1087     }\r
1088   }\r
1089 \r
1090   InitDrawingSizes(boardSize, 0);\r
1091   InitMenuChecks();\r
1092   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1093 \r
1094   /* [AS] Load textures if specified */\r
1095   InitTextures();\r
1096 \r
1097   mysrandom( (unsigned) time(NULL) );\r
1098 \r
1099   /* [AS] Restore layout */\r
1100   if( wpMoveHistory.visible ) {\r
1101       MoveHistoryPopUp();\r
1102   }\r
1103 \r
1104   if( wpEvalGraph.visible ) {\r
1105       EvalGraphPopUp();\r
1106   }\r
1107 \r
1108   if( wpEngineOutput.visible ) {\r
1109       EngineOutputPopUp();\r
1110   }\r
1111 \r
1112   /* Make the window visible; update its client area; and return "success" */\r
1113   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1114   wp.length = sizeof(WINDOWPLACEMENT);\r
1115   wp.flags = 0;\r
1116   wp.showCmd = nCmdShow;\r
1117   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1118   wp.rcNormalPosition.left = wpMain.x;\r
1119   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1120   wp.rcNormalPosition.top = wpMain.y;\r
1121   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1122   SetWindowPlacement(hwndMain, &wp);\r
1123 \r
1124   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1125 \r
1126   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1127                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1128 \r
1129   if (hwndConsole) {\r
1130 #if AOT_CONSOLE\r
1131     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1132                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1133 #endif\r
1134     ShowWindow(hwndConsole, nCmdShow);\r
1135     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1136       char buf[MSG_SIZ], *p = buf, *q;\r
1137         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1138       do {\r
1139         q = strchr(p, ';');\r
1140         if(q) *q++ = 0;\r
1141         if(*p) ChatPopUp(p);\r
1142       } while(p=q);\r
1143     }\r
1144     SetActiveWindow(hwndConsole);\r
1145   }\r
1146   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1147   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1148 \r
1149   return TRUE;\r
1150 \r
1151 }\r
1152 \r
1153 VOID\r
1154 InitMenuChecks()\r
1155 {\r
1156   HMENU hmenu = GetMenu(hwndMain);\r
1157 \r
1158   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1159                         MF_BYCOMMAND|((appData.icsActive &&\r
1160                                        *appData.icsCommPort != NULLCHAR) ?\r
1161                                       MF_ENABLED : MF_GRAYED));\r
1162   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1163                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1164                                      MF_CHECKED : MF_UNCHECKED));\r
1165 }\r
1166 \r
1167 //---------------------------------------------------------------------------------------------------------\r
1168 \r
1169 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1170 #define XBOARD FALSE\r
1171 \r
1172 #define OPTCHAR "/"\r
1173 #define SEPCHAR "="\r
1174 \r
1175 #include "args.h"\r
1176 \r
1177 // front-end part of option handling\r
1178 \r
1179 VOID\r
1180 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1181 {\r
1182   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1183   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1184   DeleteDC(hdc);\r
1185   lf->lfWidth = 0;\r
1186   lf->lfEscapement = 0;\r
1187   lf->lfOrientation = 0;\r
1188   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1189   lf->lfItalic = mfp->italic;\r
1190   lf->lfUnderline = mfp->underline;\r
1191   lf->lfStrikeOut = mfp->strikeout;\r
1192   lf->lfCharSet = mfp->charset;\r
1193   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1194   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1195   lf->lfQuality = DEFAULT_QUALITY;\r
1196   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1197     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1198 }\r
1199 \r
1200 void\r
1201 CreateFontInMF(MyFont *mf)\r
1202\r
1203   LFfromMFP(&mf->lf, &mf->mfp);\r
1204   if (mf->hf) DeleteObject(mf->hf);\r
1205   mf->hf = CreateFontIndirect(&mf->lf);\r
1206 }\r
1207 \r
1208 // [HGM] This platform-dependent table provides the location for storing the color info\r
1209 void *\r
1210 colorVariable[] = {\r
1211   &whitePieceColor, \r
1212   &blackPieceColor, \r
1213   &lightSquareColor,\r
1214   &darkSquareColor, \r
1215   &highlightSquareColor,\r
1216   &premoveHighlightColor,\r
1217   NULL,\r
1218   &consoleBackgroundColor,\r
1219   &appData.fontForeColorWhite,\r
1220   &appData.fontBackColorWhite,\r
1221   &appData.fontForeColorBlack,\r
1222   &appData.fontBackColorBlack,\r
1223   &appData.evalHistColorWhite,\r
1224   &appData.evalHistColorBlack,\r
1225   &appData.highlightArrowColor,\r
1226 };\r
1227 \r
1228 /* Command line font name parser.  NULL name means do nothing.\r
1229    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1230    For backward compatibility, syntax without the colon is also\r
1231    accepted, but font names with digits in them won't work in that case.\r
1232 */\r
1233 VOID\r
1234 ParseFontName(char *name, MyFontParams *mfp)\r
1235 {\r
1236   char *p, *q;\r
1237   if (name == NULL) return;\r
1238   p = name;\r
1239   q = strchr(p, ':');\r
1240   if (q) {\r
1241     if (q - p >= sizeof(mfp->faceName))\r
1242       ExitArgError(_("Font name too long:"), name, TRUE);\r
1243     memcpy(mfp->faceName, p, q - p);\r
1244     mfp->faceName[q - p] = NULLCHAR;\r
1245     p = q + 1;\r
1246   } else {\r
1247     q = mfp->faceName;\r
1248     while (*p && !isdigit(*p)) {\r
1249       *q++ = *p++;\r
1250       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1251         ExitArgError(_("Font name too long:"), name, TRUE);\r
1252     }\r
1253     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1254     *q = NULLCHAR;\r
1255   }\r
1256   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1257   mfp->pointSize = (float) atof(p);\r
1258   mfp->bold = (strchr(p, 'b') != NULL);\r
1259   mfp->italic = (strchr(p, 'i') != NULL);\r
1260   mfp->underline = (strchr(p, 'u') != NULL);\r
1261   mfp->strikeout = (strchr(p, 's') != NULL);\r
1262   mfp->charset = DEFAULT_CHARSET;\r
1263   q = strchr(p, 'c');\r
1264   if (q)\r
1265     mfp->charset = (BYTE) atoi(q+1);\r
1266 }\r
1267 \r
1268 void\r
1269 ParseFont(char *name, int number)\r
1270 { // wrapper to shield back-end from 'font'\r
1271   ParseFontName(name, &font[boardSize][number]->mfp);\r
1272 }\r
1273 \r
1274 void\r
1275 SetFontDefaults()\r
1276 { // in WB  we have a 2D array of fonts; this initializes their description\r
1277   int i, j;\r
1278   /* Point font array elements to structures and\r
1279      parse default font names */\r
1280   for (i=0; i<NUM_FONTS; i++) {\r
1281     for (j=0; j<NUM_SIZES; j++) {\r
1282       font[j][i] = &fontRec[j][i];\r
1283       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1284     }\r
1285   }\r
1286 }\r
1287 \r
1288 void\r
1289 CreateFonts()\r
1290 { // here we create the actual fonts from the selected descriptions\r
1291   int i, j;\r
1292   for (i=0; i<NUM_FONTS; i++) {\r
1293     for (j=0; j<NUM_SIZES; j++) {\r
1294       CreateFontInMF(font[j][i]);\r
1295     }\r
1296   }\r
1297 }\r
1298 /* Color name parser.\r
1299    X version accepts X color names, but this one\r
1300    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1301 COLORREF\r
1302 ParseColorName(char *name)\r
1303 {\r
1304   int red, green, blue, count;\r
1305   char buf[MSG_SIZ];\r
1306 \r
1307   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1308   if (count != 3) {\r
1309     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1310       &red, &green, &blue);\r
1311   }\r
1312   if (count != 3) {\r
1313     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1314     DisplayError(buf, 0);\r
1315     return RGB(0, 0, 0);\r
1316   }\r
1317   return PALETTERGB(red, green, blue);\r
1318 }\r
1319 \r
1320 void\r
1321 ParseColor(int n, char *name)\r
1322 { // for WinBoard the color is an int, which needs to be derived from the string\r
1323   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1324 }\r
1325 \r
1326 void\r
1327 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1328 {\r
1329   char *e = argValue;\r
1330   int eff = 0;\r
1331 \r
1332   while (*e) {\r
1333     if (*e == 'b')      eff |= CFE_BOLD;\r
1334     else if (*e == 'i') eff |= CFE_ITALIC;\r
1335     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1336     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1337     else if (*e == '#' || isdigit(*e)) break;\r
1338     e++;\r
1339   }\r
1340   *effects = eff;\r
1341   *color   = ParseColorName(e);\r
1342 }\r
1343 \r
1344 void\r
1345 ParseTextAttribs(ColorClass cc, char *s)\r
1346 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1347     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1348     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1349 }\r
1350 \r
1351 void\r
1352 ParseBoardSize(void *addr, char *name)\r
1353 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1354   BoardSize bs = SizeTiny;\r
1355   while (sizeInfo[bs].name != NULL) {\r
1356     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1357         *(BoardSize *)addr = bs;\r
1358         return;\r
1359     }\r
1360     bs++;\r
1361   }\r
1362   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1363 }\r
1364 \r
1365 void\r
1366 LoadAllSounds()\r
1367 { // [HGM] import name from appData first\r
1368   ColorClass cc;\r
1369   SoundClass sc;\r
1370   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1371     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1372     textAttribs[cc].sound.data = NULL;\r
1373     MyLoadSound(&textAttribs[cc].sound);\r
1374   }\r
1375   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1376     textAttribs[cc].sound.name = strdup("");\r
1377     textAttribs[cc].sound.data = NULL;\r
1378   }\r
1379   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1380     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1381     sounds[sc].data = NULL;\r
1382     MyLoadSound(&sounds[sc]);\r
1383   }\r
1384 }\r
1385 \r
1386 void\r
1387 SetCommPortDefaults()\r
1388 {\r
1389    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1390   dcb.DCBlength = sizeof(DCB);\r
1391   dcb.BaudRate = 9600;\r
1392   dcb.fBinary = TRUE;\r
1393   dcb.fParity = FALSE;\r
1394   dcb.fOutxCtsFlow = FALSE;\r
1395   dcb.fOutxDsrFlow = FALSE;\r
1396   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1397   dcb.fDsrSensitivity = FALSE;\r
1398   dcb.fTXContinueOnXoff = TRUE;\r
1399   dcb.fOutX = FALSE;\r
1400   dcb.fInX = FALSE;\r
1401   dcb.fNull = FALSE;\r
1402   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1403   dcb.fAbortOnError = FALSE;\r
1404   dcb.ByteSize = 7;\r
1405   dcb.Parity = SPACEPARITY;\r
1406   dcb.StopBits = ONESTOPBIT;\r
1407 }\r
1408 \r
1409 // [HGM] args: these three cases taken out to stay in front-end\r
1410 void\r
1411 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1412 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1413         // while the curent board size determines the element. This system should be ported to XBoard.\r
1414         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1415         int bs;\r
1416         for (bs=0; bs<NUM_SIZES; bs++) {\r
1417           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1418           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1419           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1420             ad->argName, mfp->faceName, mfp->pointSize,\r
1421             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1422             mfp->bold ? "b" : "",\r
1423             mfp->italic ? "i" : "",\r
1424             mfp->underline ? "u" : "",\r
1425             mfp->strikeout ? "s" : "",\r
1426             (int)mfp->charset);\r
1427         }\r
1428       }\r
1429 \r
1430 void\r
1431 ExportSounds()\r
1432 { // [HGM] copy the names from the internal WB variables to appData\r
1433   ColorClass cc;\r
1434   SoundClass sc;\r
1435   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1436     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1437   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1438     (&appData.soundMove)[sc] = sounds[sc].name;\r
1439 }\r
1440 \r
1441 void\r
1442 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1443 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1444         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1445         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1446           (ta->effects & CFE_BOLD) ? "b" : "",\r
1447           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1448           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1449           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1450           (ta->effects) ? " " : "",\r
1451           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1452       }\r
1453 \r
1454 void\r
1455 SaveColor(FILE *f, ArgDescriptor *ad)\r
1456 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1457         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1458         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1459           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1460 }\r
1461 \r
1462 void\r
1463 SaveBoardSize(FILE *f, char *name, void *addr)\r
1464 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1465   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1466 }\r
1467 \r
1468 void\r
1469 ParseCommPortSettings(char *s)\r
1470 { // wrapper to keep dcb from back-end\r
1471   ParseCommSettings(s, &dcb);\r
1472 }\r
1473 \r
1474 void\r
1475 GetWindowCoords()\r
1476 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1477   GetActualPlacement(hwndMain, &wpMain);\r
1478   GetActualPlacement(hwndConsole, &wpConsole);\r
1479   GetActualPlacement(commentDialog, &wpComment);\r
1480   GetActualPlacement(editTagsDialog, &wpTags);\r
1481   GetActualPlacement(gameListDialog, &wpGameList);\r
1482   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1483   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1484   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1485 }\r
1486 \r
1487 void\r
1488 PrintCommPortSettings(FILE *f, char *name)\r
1489 { // wrapper to shield back-end from DCB\r
1490       PrintCommSettings(f, name, &dcb);\r
1491 }\r
1492 \r
1493 int\r
1494 MySearchPath(char *installDir, char *name, char *fullname)\r
1495 {\r
1496   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1497   if(name[0]== '%') {\r
1498     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1499     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1500       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1501       *strchr(buf, '%') = 0;\r
1502       strcat(fullname, getenv(buf));\r
1503       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1504     }\r
1505     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1506     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1507     return (int) strlen(fullname);\r
1508   }\r
1509   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1510 }\r
1511 \r
1512 int\r
1513 MyGetFullPathName(char *name, char *fullname)\r
1514 {\r
1515   char *dummy;\r
1516   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1517 }\r
1518 \r
1519 int\r
1520 MainWindowUp()\r
1521 { // [HGM] args: allows testing if main window is realized from back-end\r
1522   return hwndMain != NULL;\r
1523 }\r
1524 \r
1525 void\r
1526 PopUpStartupDialog()\r
1527 {\r
1528     FARPROC lpProc;\r
1529     \r
1530     LoadLanguageFile(appData.language);\r
1531     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1532     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1533     FreeProcInstance(lpProc);\r
1534 }\r
1535 \r
1536 /*---------------------------------------------------------------------------*\\r
1537  *\r
1538  * GDI board drawing routines\r
1539  *\r
1540 \*---------------------------------------------------------------------------*/\r
1541 \r
1542 /* [AS] Draw square using background texture */\r
1543 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1544 {\r
1545     XFORM   x;\r
1546 \r
1547     if( mode == 0 ) {\r
1548         return; /* Should never happen! */\r
1549     }\r
1550 \r
1551     SetGraphicsMode( dst, GM_ADVANCED );\r
1552 \r
1553     switch( mode ) {\r
1554     case 1:\r
1555         /* Identity */\r
1556         break;\r
1557     case 2:\r
1558         /* X reflection */\r
1559         x.eM11 = -1.0;\r
1560         x.eM12 = 0;\r
1561         x.eM21 = 0;\r
1562         x.eM22 = 1.0;\r
1563         x.eDx = (FLOAT) dw + dx - 1;\r
1564         x.eDy = 0;\r
1565         dx = 0;\r
1566         SetWorldTransform( dst, &x );\r
1567         break;\r
1568     case 3:\r
1569         /* Y reflection */\r
1570         x.eM11 = 1.0;\r
1571         x.eM12 = 0;\r
1572         x.eM21 = 0;\r
1573         x.eM22 = -1.0;\r
1574         x.eDx = 0;\r
1575         x.eDy = (FLOAT) dh + dy - 1;\r
1576         dy = 0;\r
1577         SetWorldTransform( dst, &x );\r
1578         break;\r
1579     case 4:\r
1580         /* X/Y flip */\r
1581         x.eM11 = 0;\r
1582         x.eM12 = 1.0;\r
1583         x.eM21 = 1.0;\r
1584         x.eM22 = 0;\r
1585         x.eDx = (FLOAT) dx;\r
1586         x.eDy = (FLOAT) dy;\r
1587         dx = 0;\r
1588         dy = 0;\r
1589         SetWorldTransform( dst, &x );\r
1590         break;\r
1591     }\r
1592 \r
1593     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1594 \r
1595     x.eM11 = 1.0;\r
1596     x.eM12 = 0;\r
1597     x.eM21 = 0;\r
1598     x.eM22 = 1.0;\r
1599     x.eDx = 0;\r
1600     x.eDy = 0;\r
1601     SetWorldTransform( dst, &x );\r
1602 \r
1603     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1604 }\r
1605 \r
1606 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1607 enum {\r
1608     PM_WP = (int) WhitePawn, \r
1609     PM_WN = (int) WhiteKnight, \r
1610     PM_WB = (int) WhiteBishop, \r
1611     PM_WR = (int) WhiteRook, \r
1612     PM_WQ = (int) WhiteQueen, \r
1613     PM_WF = (int) WhiteFerz, \r
1614     PM_WW = (int) WhiteWazir, \r
1615     PM_WE = (int) WhiteAlfil, \r
1616     PM_WM = (int) WhiteMan, \r
1617     PM_WO = (int) WhiteCannon, \r
1618     PM_WU = (int) WhiteUnicorn, \r
1619     PM_WH = (int) WhiteNightrider, \r
1620     PM_WA = (int) WhiteAngel, \r
1621     PM_WC = (int) WhiteMarshall, \r
1622     PM_WAB = (int) WhiteCardinal, \r
1623     PM_WD = (int) WhiteDragon, \r
1624     PM_WL = (int) WhiteLance, \r
1625     PM_WS = (int) WhiteCobra, \r
1626     PM_WV = (int) WhiteFalcon, \r
1627     PM_WSG = (int) WhiteSilver, \r
1628     PM_WG = (int) WhiteGrasshopper, \r
1629     PM_WK = (int) WhiteKing,\r
1630     PM_BP = (int) BlackPawn, \r
1631     PM_BN = (int) BlackKnight, \r
1632     PM_BB = (int) BlackBishop, \r
1633     PM_BR = (int) BlackRook, \r
1634     PM_BQ = (int) BlackQueen, \r
1635     PM_BF = (int) BlackFerz, \r
1636     PM_BW = (int) BlackWazir, \r
1637     PM_BE = (int) BlackAlfil, \r
1638     PM_BM = (int) BlackMan,\r
1639     PM_BO = (int) BlackCannon, \r
1640     PM_BU = (int) BlackUnicorn, \r
1641     PM_BH = (int) BlackNightrider, \r
1642     PM_BA = (int) BlackAngel, \r
1643     PM_BC = (int) BlackMarshall, \r
1644     PM_BG = (int) BlackGrasshopper, \r
1645     PM_BAB = (int) BlackCardinal,\r
1646     PM_BD = (int) BlackDragon,\r
1647     PM_BL = (int) BlackLance,\r
1648     PM_BS = (int) BlackCobra,\r
1649     PM_BV = (int) BlackFalcon,\r
1650     PM_BSG = (int) BlackSilver,\r
1651     PM_BK = (int) BlackKing\r
1652 };\r
1653 \r
1654 static HFONT hPieceFont = NULL;\r
1655 static HBITMAP hPieceMask[(int) EmptySquare];\r
1656 static HBITMAP hPieceFace[(int) EmptySquare];\r
1657 static int fontBitmapSquareSize = 0;\r
1658 static char pieceToFontChar[(int) EmptySquare] =\r
1659                               { 'p', 'n', 'b', 'r', 'q', \r
1660                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1661                       'k', 'o', 'm', 'v', 't', 'w', \r
1662                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1663                                                               'l' };\r
1664 \r
1665 extern BOOL SetCharTable( char *table, const char * map );\r
1666 /* [HGM] moved to backend.c */\r
1667 \r
1668 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1669 {\r
1670     HBRUSH hbrush;\r
1671     BYTE r1 = GetRValue( color );\r
1672     BYTE g1 = GetGValue( color );\r
1673     BYTE b1 = GetBValue( color );\r
1674     BYTE r2 = r1 / 2;\r
1675     BYTE g2 = g1 / 2;\r
1676     BYTE b2 = b1 / 2;\r
1677     RECT rc;\r
1678 \r
1679     /* Create a uniform background first */\r
1680     hbrush = CreateSolidBrush( color );\r
1681     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1682     FillRect( hdc, &rc, hbrush );\r
1683     DeleteObject( hbrush );\r
1684     \r
1685     if( mode == 1 ) {\r
1686         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1687         int steps = squareSize / 2;\r
1688         int i;\r
1689 \r
1690         for( i=0; i<steps; i++ ) {\r
1691             BYTE r = r1 - (r1-r2) * i / steps;\r
1692             BYTE g = g1 - (g1-g2) * i / steps;\r
1693             BYTE b = b1 - (b1-b2) * i / steps;\r
1694 \r
1695             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1696             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1697             FillRect( hdc, &rc, hbrush );\r
1698             DeleteObject(hbrush);\r
1699         }\r
1700     }\r
1701     else if( mode == 2 ) {\r
1702         /* Diagonal gradient, good more or less for every piece */\r
1703         POINT triangle[3];\r
1704         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1705         HBRUSH hbrush_old;\r
1706         int steps = squareSize;\r
1707         int i;\r
1708 \r
1709         triangle[0].x = squareSize - steps;\r
1710         triangle[0].y = squareSize;\r
1711         triangle[1].x = squareSize;\r
1712         triangle[1].y = squareSize;\r
1713         triangle[2].x = squareSize;\r
1714         triangle[2].y = squareSize - steps;\r
1715 \r
1716         for( i=0; i<steps; i++ ) {\r
1717             BYTE r = r1 - (r1-r2) * i / steps;\r
1718             BYTE g = g1 - (g1-g2) * i / steps;\r
1719             BYTE b = b1 - (b1-b2) * i / steps;\r
1720 \r
1721             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1722             hbrush_old = SelectObject( hdc, hbrush );\r
1723             Polygon( hdc, triangle, 3 );\r
1724             SelectObject( hdc, hbrush_old );\r
1725             DeleteObject(hbrush);\r
1726             triangle[0].x++;\r
1727             triangle[2].y++;\r
1728         }\r
1729 \r
1730         SelectObject( hdc, hpen );\r
1731     }\r
1732 }\r
1733 \r
1734 /*\r
1735     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1736     seems to work ok. The main problem here is to find the "inside" of a chess\r
1737     piece: follow the steps as explained below.\r
1738 */\r
1739 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1740 {\r
1741     HBITMAP hbm;\r
1742     HBITMAP hbm_old;\r
1743     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1744     RECT rc;\r
1745     SIZE sz;\r
1746     POINT pt;\r
1747     int backColor = whitePieceColor; \r
1748     int foreColor = blackPieceColor;\r
1749     \r
1750     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1751         backColor = appData.fontBackColorWhite;\r
1752         foreColor = appData.fontForeColorWhite;\r
1753     }\r
1754     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1755         backColor = appData.fontBackColorBlack;\r
1756         foreColor = appData.fontForeColorBlack;\r
1757     }\r
1758 \r
1759     /* Mask */\r
1760     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1761 \r
1762     hbm_old = SelectObject( hdc, hbm );\r
1763 \r
1764     rc.left = 0;\r
1765     rc.top = 0;\r
1766     rc.right = squareSize;\r
1767     rc.bottom = squareSize;\r
1768 \r
1769     /* Step 1: background is now black */\r
1770     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1771 \r
1772     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1773 \r
1774     pt.x = (squareSize - sz.cx) / 2;\r
1775     pt.y = (squareSize - sz.cy) / 2;\r
1776 \r
1777     SetBkMode( hdc, TRANSPARENT );\r
1778     SetTextColor( hdc, chroma );\r
1779     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1780     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1781 \r
1782     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1783     /* Step 3: the area outside the piece is filled with white */\r
1784 //    FloodFill( hdc, 0, 0, chroma );\r
1785     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1786     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1787     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1788     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1789     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1790     /* \r
1791         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1792         but if the start point is not inside the piece we're lost!\r
1793         There should be a better way to do this... if we could create a region or path\r
1794         from the fill operation we would be fine for example.\r
1795     */\r
1796 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1797     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1798 \r
1799     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1800         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1801         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1802 \r
1803         SelectObject( dc2, bm2 );\r
1804         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1805         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1806         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1808         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1809 \r
1810         DeleteDC( dc2 );\r
1811         DeleteObject( bm2 );\r
1812     }\r
1813 \r
1814     SetTextColor( hdc, 0 );\r
1815     /* \r
1816         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1817         draw the piece again in black for safety.\r
1818     */\r
1819     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1820 \r
1821     SelectObject( hdc, hbm_old );\r
1822 \r
1823     if( hPieceMask[index] != NULL ) {\r
1824         DeleteObject( hPieceMask[index] );\r
1825     }\r
1826 \r
1827     hPieceMask[index] = hbm;\r
1828 \r
1829     /* Face */\r
1830     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1831 \r
1832     SelectObject( hdc, hbm );\r
1833 \r
1834     {\r
1835         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1836         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1837         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1838 \r
1839         SelectObject( dc1, hPieceMask[index] );\r
1840         SelectObject( dc2, bm2 );\r
1841         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1842         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1843         \r
1844         /* \r
1845             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1846             the piece background and deletes (makes transparent) the rest.\r
1847             Thanks to that mask, we are free to paint the background with the greates\r
1848             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1849             We use this, to make gradients and give the pieces a "roundish" look.\r
1850         */\r
1851         SetPieceBackground( hdc, backColor, 2 );\r
1852         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1853 \r
1854         DeleteDC( dc2 );\r
1855         DeleteDC( dc1 );\r
1856         DeleteObject( bm2 );\r
1857     }\r
1858 \r
1859     SetTextColor( hdc, foreColor );\r
1860     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1861 \r
1862     SelectObject( hdc, hbm_old );\r
1863 \r
1864     if( hPieceFace[index] != NULL ) {\r
1865         DeleteObject( hPieceFace[index] );\r
1866     }\r
1867 \r
1868     hPieceFace[index] = hbm;\r
1869 }\r
1870 \r
1871 static int TranslatePieceToFontPiece( int piece )\r
1872 {\r
1873     switch( piece ) {\r
1874     case BlackPawn:\r
1875         return PM_BP;\r
1876     case BlackKnight:\r
1877         return PM_BN;\r
1878     case BlackBishop:\r
1879         return PM_BB;\r
1880     case BlackRook:\r
1881         return PM_BR;\r
1882     case BlackQueen:\r
1883         return PM_BQ;\r
1884     case BlackKing:\r
1885         return PM_BK;\r
1886     case WhitePawn:\r
1887         return PM_WP;\r
1888     case WhiteKnight:\r
1889         return PM_WN;\r
1890     case WhiteBishop:\r
1891         return PM_WB;\r
1892     case WhiteRook:\r
1893         return PM_WR;\r
1894     case WhiteQueen:\r
1895         return PM_WQ;\r
1896     case WhiteKing:\r
1897         return PM_WK;\r
1898 \r
1899     case BlackAngel:\r
1900         return PM_BA;\r
1901     case BlackMarshall:\r
1902         return PM_BC;\r
1903     case BlackFerz:\r
1904         return PM_BF;\r
1905     case BlackNightrider:\r
1906         return PM_BH;\r
1907     case BlackAlfil:\r
1908         return PM_BE;\r
1909     case BlackWazir:\r
1910         return PM_BW;\r
1911     case BlackUnicorn:\r
1912         return PM_BU;\r
1913     case BlackCannon:\r
1914         return PM_BO;\r
1915     case BlackGrasshopper:\r
1916         return PM_BG;\r
1917     case BlackMan:\r
1918         return PM_BM;\r
1919     case BlackSilver:\r
1920         return PM_BSG;\r
1921     case BlackLance:\r
1922         return PM_BL;\r
1923     case BlackFalcon:\r
1924         return PM_BV;\r
1925     case BlackCobra:\r
1926         return PM_BS;\r
1927     case BlackCardinal:\r
1928         return PM_BAB;\r
1929     case BlackDragon:\r
1930         return PM_BD;\r
1931 \r
1932     case WhiteAngel:\r
1933         return PM_WA;\r
1934     case WhiteMarshall:\r
1935         return PM_WC;\r
1936     case WhiteFerz:\r
1937         return PM_WF;\r
1938     case WhiteNightrider:\r
1939         return PM_WH;\r
1940     case WhiteAlfil:\r
1941         return PM_WE;\r
1942     case WhiteWazir:\r
1943         return PM_WW;\r
1944     case WhiteUnicorn:\r
1945         return PM_WU;\r
1946     case WhiteCannon:\r
1947         return PM_WO;\r
1948     case WhiteGrasshopper:\r
1949         return PM_WG;\r
1950     case WhiteMan:\r
1951         return PM_WM;\r
1952     case WhiteSilver:\r
1953         return PM_WSG;\r
1954     case WhiteLance:\r
1955         return PM_WL;\r
1956     case WhiteFalcon:\r
1957         return PM_WV;\r
1958     case WhiteCobra:\r
1959         return PM_WS;\r
1960     case WhiteCardinal:\r
1961         return PM_WAB;\r
1962     case WhiteDragon:\r
1963         return PM_WD;\r
1964     }\r
1965 \r
1966     return 0;\r
1967 }\r
1968 \r
1969 void CreatePiecesFromFont()\r
1970 {\r
1971     LOGFONT lf;\r
1972     HDC hdc_window = NULL;\r
1973     HDC hdc = NULL;\r
1974     HFONT hfont_old;\r
1975     int fontHeight;\r
1976     int i;\r
1977 \r
1978     if( fontBitmapSquareSize < 0 ) {\r
1979         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1980         return;\r
1981     }\r
1982 \r
1983     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
1984             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1985         fontBitmapSquareSize = -1;\r
1986         return;\r
1987     }\r
1988 \r
1989     if( fontBitmapSquareSize != squareSize ) {\r
1990         hdc_window = GetDC( hwndMain );\r
1991         hdc = CreateCompatibleDC( hdc_window );\r
1992 \r
1993         if( hPieceFont != NULL ) {\r
1994             DeleteObject( hPieceFont );\r
1995         }\r
1996         else {\r
1997             for( i=0; i<=(int)BlackKing; i++ ) {\r
1998                 hPieceMask[i] = NULL;\r
1999                 hPieceFace[i] = NULL;\r
2000             }\r
2001         }\r
2002 \r
2003         fontHeight = 75;\r
2004 \r
2005         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2006             fontHeight = appData.fontPieceSize;\r
2007         }\r
2008 \r
2009         fontHeight = (fontHeight * squareSize) / 100;\r
2010 \r
2011         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2012         lf.lfWidth = 0;\r
2013         lf.lfEscapement = 0;\r
2014         lf.lfOrientation = 0;\r
2015         lf.lfWeight = FW_NORMAL;\r
2016         lf.lfItalic = 0;\r
2017         lf.lfUnderline = 0;\r
2018         lf.lfStrikeOut = 0;\r
2019         lf.lfCharSet = DEFAULT_CHARSET;\r
2020         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2021         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2022         lf.lfQuality = PROOF_QUALITY;\r
2023         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2024         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2025         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2026 \r
2027         hPieceFont = CreateFontIndirect( &lf );\r
2028 \r
2029         if( hPieceFont == NULL ) {\r
2030             fontBitmapSquareSize = -2;\r
2031         }\r
2032         else {\r
2033             /* Setup font-to-piece character table */\r
2034             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2035                 /* No (or wrong) global settings, try to detect the font */\r
2036                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2037                     /* Alpha */\r
2038                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2039                 }\r
2040                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2041                     /* DiagramTT* family */\r
2042                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2043                 }\r
2044                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2045                     /* Fairy symbols */\r
2046                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2047                 }\r
2048                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2049                     /* Good Companion (Some characters get warped as literal :-( */\r
2050                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2051                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2052                     SetCharTable(pieceToFontChar, s);\r
2053                 }\r
2054                 else {\r
2055                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2056                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2057                 }\r
2058             }\r
2059 \r
2060             /* Create bitmaps */\r
2061             hfont_old = SelectObject( hdc, hPieceFont );\r
2062             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2063                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2064                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2065 \r
2066             SelectObject( hdc, hfont_old );\r
2067 \r
2068             fontBitmapSquareSize = squareSize;\r
2069         }\r
2070     }\r
2071 \r
2072     if( hdc != NULL ) {\r
2073         DeleteDC( hdc );\r
2074     }\r
2075 \r
2076     if( hdc_window != NULL ) {\r
2077         ReleaseDC( hwndMain, hdc_window );\r
2078     }\r
2079 }\r
2080 \r
2081 HBITMAP\r
2082 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2083 {\r
2084   char name[128];\r
2085 \r
2086     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2087   if (gameInfo.event &&\r
2088       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2089       strcmp(name, "k80s") == 0) {\r
2090     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2091   }\r
2092   return LoadBitmap(hinst, name);\r
2093 }\r
2094 \r
2095 \r
2096 /* Insert a color into the program's logical palette\r
2097    structure.  This code assumes the given color is\r
2098    the result of the RGB or PALETTERGB macro, and it\r
2099    knows how those macros work (which is documented).\r
2100 */\r
2101 VOID\r
2102 InsertInPalette(COLORREF color)\r
2103 {\r
2104   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2105 \r
2106   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2107     DisplayFatalError(_("Too many colors"), 0, 1);\r
2108     pLogPal->palNumEntries--;\r
2109     return;\r
2110   }\r
2111 \r
2112   pe->peFlags = (char) 0;\r
2113   pe->peRed = (char) (0xFF & color);\r
2114   pe->peGreen = (char) (0xFF & (color >> 8));\r
2115   pe->peBlue = (char) (0xFF & (color >> 16));\r
2116   return;\r
2117 }\r
2118 \r
2119 \r
2120 VOID\r
2121 InitDrawingColors()\r
2122 {\r
2123   if (pLogPal == NULL) {\r
2124     /* Allocate enough memory for a logical palette with\r
2125      * PALETTESIZE entries and set the size and version fields\r
2126      * of the logical palette structure.\r
2127      */\r
2128     pLogPal = (NPLOGPALETTE)\r
2129       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2130                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2131     pLogPal->palVersion    = 0x300;\r
2132   }\r
2133   pLogPal->palNumEntries = 0;\r
2134 \r
2135   InsertInPalette(lightSquareColor);\r
2136   InsertInPalette(darkSquareColor);\r
2137   InsertInPalette(whitePieceColor);\r
2138   InsertInPalette(blackPieceColor);\r
2139   InsertInPalette(highlightSquareColor);\r
2140   InsertInPalette(premoveHighlightColor);\r
2141 \r
2142   /*  create a logical color palette according the information\r
2143    *  in the LOGPALETTE structure.\r
2144    */\r
2145   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2146 \r
2147   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2148   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2149   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2150   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2151   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2152   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2153   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2154   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2155   /* [AS] Force rendering of the font-based pieces */\r
2156   if( fontBitmapSquareSize > 0 ) {\r
2157     fontBitmapSquareSize = 0;\r
2158   }\r
2159 }\r
2160 \r
2161 \r
2162 int\r
2163 BoardWidth(int boardSize, int n)\r
2164 { /* [HGM] argument n added to allow different width and height */\r
2165   int lineGap = sizeInfo[boardSize].lineGap;\r
2166 \r
2167   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2168       lineGap = appData.overrideLineGap;\r
2169   }\r
2170 \r
2171   return (n + 1) * lineGap +\r
2172           n * sizeInfo[boardSize].squareSize;\r
2173 }\r
2174 \r
2175 /* Respond to board resize by dragging edge */\r
2176 VOID\r
2177 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2178 {\r
2179   BoardSize newSize = NUM_SIZES - 1;\r
2180   static int recurse = 0;\r
2181   if (IsIconic(hwndMain)) return;\r
2182   if (recurse > 0) return;\r
2183   recurse++;\r
2184   while (newSize > 0) {\r
2185         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2186         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2187            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2188     newSize--;\r
2189   } \r
2190   boardSize = newSize;\r
2191   InitDrawingSizes(boardSize, flags);\r
2192   recurse--;\r
2193 }\r
2194 \r
2195 \r
2196 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2197 \r
2198 VOID\r
2199 InitDrawingSizes(BoardSize boardSize, int flags)\r
2200 {\r
2201   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2202   ChessSquare piece;\r
2203   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2204   HDC hdc;\r
2205   SIZE clockSize, messageSize;\r
2206   HFONT oldFont;\r
2207   char buf[MSG_SIZ];\r
2208   char *str;\r
2209   HMENU hmenu = GetMenu(hwndMain);\r
2210   RECT crect, wrect, oldRect;\r
2211   int offby;\r
2212   LOGBRUSH logbrush;\r
2213 \r
2214   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2215   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2216 \r
2217   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2218   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2219 \r
2220   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2221   oldRect.top = wpMain.y;\r
2222   oldRect.right = wpMain.x + wpMain.width;\r
2223   oldRect.bottom = wpMain.y + wpMain.height;\r
2224 \r
2225   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2226   smallLayout = sizeInfo[boardSize].smallLayout;\r
2227   squareSize = sizeInfo[boardSize].squareSize;\r
2228   lineGap = sizeInfo[boardSize].lineGap;\r
2229   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2230 \r
2231   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2232       lineGap = appData.overrideLineGap;\r
2233   }\r
2234 \r
2235   if (tinyLayout != oldTinyLayout) {\r
2236     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2237     if (tinyLayout) {\r
2238       style &= ~WS_SYSMENU;\r
2239       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2240                  "&Minimize\tCtrl+F4");\r
2241     } else {\r
2242       style |= WS_SYSMENU;\r
2243       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2244     }\r
2245     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2246 \r
2247     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2248       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2249         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2250     }\r
2251     DrawMenuBar(hwndMain);\r
2252   }\r
2253 \r
2254   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2255   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2256 \r
2257   /* Get text area sizes */\r
2258   hdc = GetDC(hwndMain);\r
2259   if (appData.clockMode) {\r
2260     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2261   } else {\r
2262     snprintf(buf, MSG_SIZ, _("White"));\r
2263   }\r
2264   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2265   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2266   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2267   str = _("We only care about the height here");\r
2268   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2269   SelectObject(hdc, oldFont);\r
2270   ReleaseDC(hwndMain, hdc);\r
2271 \r
2272   /* Compute where everything goes */\r
2273   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2274         /* [HGM] logo: if either logo is on, reserve space for it */\r
2275         logoHeight =  2*clockSize.cy;\r
2276         leftLogoRect.left   = OUTER_MARGIN;\r
2277         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2278         leftLogoRect.top    = OUTER_MARGIN;\r
2279         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2280 \r
2281         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2282         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2283         rightLogoRect.top    = OUTER_MARGIN;\r
2284         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2285 \r
2286 \r
2287     whiteRect.left = leftLogoRect.right;\r
2288     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + logoHeight;\r
2291 \r
2292     blackRect.right = rightLogoRect.left;\r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296   } else {\r
2297     whiteRect.left = OUTER_MARGIN;\r
2298     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2299     whiteRect.top = OUTER_MARGIN;\r
2300     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2301 \r
2302     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2303     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2304     blackRect.top = whiteRect.top;\r
2305     blackRect.bottom = whiteRect.bottom;\r
2306 \r
2307     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2308   }\r
2309 \r
2310   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2311   if (appData.showButtonBar) {\r
2312     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2313       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2314   } else {\r
2315     messageRect.right = OUTER_MARGIN + boardWidth;\r
2316   }\r
2317   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2318   messageRect.bottom = messageRect.top + messageSize.cy;\r
2319 \r
2320   boardRect.left = OUTER_MARGIN;\r
2321   boardRect.right = boardRect.left + boardWidth;\r
2322   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2323   boardRect.bottom = boardRect.top + boardHeight;\r
2324 \r
2325   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2326   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2327   oldBoardSize = boardSize;\r
2328   oldTinyLayout = tinyLayout;\r
2329   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2330   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2331     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2332   winW *= 1 + twoBoards;\r
2333   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2334   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2335   wpMain.height = winH; //       without disturbing window attachments\r
2336   GetWindowRect(hwndMain, &wrect);\r
2337   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2338                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2339 \r
2340   // [HGM] placement: let attached windows follow size change.\r
2341   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2344   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2345   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2346 \r
2347   /* compensate if menu bar wrapped */\r
2348   GetClientRect(hwndMain, &crect);\r
2349   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2350   wpMain.height += offby;\r
2351   switch (flags) {\r
2352   case WMSZ_TOPLEFT:\r
2353     SetWindowPos(hwndMain, NULL, \r
2354                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2355                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2356     break;\r
2357 \r
2358   case WMSZ_TOPRIGHT:\r
2359   case WMSZ_TOP:\r
2360     SetWindowPos(hwndMain, NULL, \r
2361                  wrect.left, wrect.bottom - wpMain.height, \r
2362                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2363     break;\r
2364 \r
2365   case WMSZ_BOTTOMLEFT:\r
2366   case WMSZ_LEFT:\r
2367     SetWindowPos(hwndMain, NULL, \r
2368                  wrect.right - wpMain.width, wrect.top, \r
2369                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2370     break;\r
2371 \r
2372   case WMSZ_BOTTOMRIGHT:\r
2373   case WMSZ_BOTTOM:\r
2374   case WMSZ_RIGHT:\r
2375   default:\r
2376     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2377                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2378     break;\r
2379   }\r
2380 \r
2381   hwndPause = NULL;\r
2382   for (i = 0; i < N_BUTTONS; i++) {\r
2383     if (buttonDesc[i].hwnd != NULL) {\r
2384       DestroyWindow(buttonDesc[i].hwnd);\r
2385       buttonDesc[i].hwnd = NULL;\r
2386     }\r
2387     if (appData.showButtonBar) {\r
2388       buttonDesc[i].hwnd =\r
2389         CreateWindow("BUTTON", buttonDesc[i].label,\r
2390                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2391                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2392                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2393                      (HMENU) buttonDesc[i].id,\r
2394                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2395       if (tinyLayout) {\r
2396         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2397                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2398                     MAKELPARAM(FALSE, 0));\r
2399       }\r
2400       if (buttonDesc[i].id == IDM_Pause)\r
2401         hwndPause = buttonDesc[i].hwnd;\r
2402       buttonDesc[i].wndproc = (WNDPROC)\r
2403         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2404     }\r
2405   }\r
2406   if (gridPen != NULL) DeleteObject(gridPen);\r
2407   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2408   if (premovePen != NULL) DeleteObject(premovePen);\r
2409   if (lineGap != 0) {\r
2410     logbrush.lbStyle = BS_SOLID;\r
2411     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2412     gridPen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415     logbrush.lbColor = highlightSquareColor;\r
2416     highlightPen =\r
2417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2418                    lineGap, &logbrush, 0, NULL);\r
2419 \r
2420     logbrush.lbColor = premoveHighlightColor; \r
2421     premovePen =\r
2422       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2423                    lineGap, &logbrush, 0, NULL);\r
2424 \r
2425     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2426     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2427       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2428       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2429         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2431         BOARD_WIDTH * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2435       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2436       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2437         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2438         lineGap / 2 + (i * (squareSize + lineGap));\r
2439       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2440         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2441       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2442     }\r
2443   }\r
2444 \r
2445   /* [HGM] Licensing requirement */\r
2446 #ifdef GOTHIC\r
2447   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2448 #endif\r
2449 #ifdef FALCON\r
2450   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2451 #endif\r
2452   GothicPopUp( "", VariantNormal);\r
2453 \r
2454 \r
2455 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2456 \r
2457   /* Load piece bitmaps for this board size */\r
2458   for (i=0; i<=2; i++) {\r
2459     for (piece = WhitePawn;\r
2460          (int) piece < (int) BlackPawn;\r
2461          piece = (ChessSquare) ((int) piece + 1)) {\r
2462       if (pieceBitmap[i][piece] != NULL)\r
2463         DeleteObject(pieceBitmap[i][piece]);\r
2464     }\r
2465   }\r
2466 \r
2467   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2468   // Orthodox Chess pieces\r
2469   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2470   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2471   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2472   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2474   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2475   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2476   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2477   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2478   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2479   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2480   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2481   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2482   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2483   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2484   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2485     // in Shogi, Hijack the unused Queen for Lance\r
2486     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2487     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2488     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2489   } else {\r
2490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2493   }\r
2494 \r
2495   if(squareSize <= 72 && squareSize >= 33) { \r
2496     /* A & C are available in most sizes now */\r
2497     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2498       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2499       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2500       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2501       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2502       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2503       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2504       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2505       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2506       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2507       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2508       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2509       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2510     } else { // Smirf-like\r
2511       if(gameInfo.variant == VariantSChess) {\r
2512         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2513         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2514         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2515       } else {\r
2516         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2517         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2518         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2519       }\r
2520     }\r
2521     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2525     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2526       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2527       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2528       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2529     } else { // WinBoard standard\r
2530       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2531       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2532       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2533     }\r
2534   }\r
2535 \r
2536 \r
2537   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2538     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2539     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2540     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2541     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2542     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2543     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2545     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2546     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2547     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2548     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2549     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2553     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2554     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2555     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2556     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2557     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2558     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2559     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2560     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2561     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2562     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2563     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2564     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2565     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2566     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2567     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2568 \r
2569     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2570       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2571       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2572       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2574       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2575       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2576       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2577       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2578       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2579       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2580       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2581       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2582     } else {\r
2583       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2584       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2585       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2586       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2587       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2588       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2589       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2590       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2591       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2592       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2593       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2594       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2595     }\r
2596 \r
2597   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2598     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2599     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2600     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2601     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2602     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2603     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2604     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2605     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2606     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2607     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2608     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2609     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2610     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2611     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2612   }\r
2613 \r
2614 \r
2615   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2616   /* special Shogi support in this size */\r
2617   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2618       for (piece = WhitePawn;\r
2619            (int) piece < (int) BlackPawn;\r
2620            piece = (ChessSquare) ((int) piece + 1)) {\r
2621         if (pieceBitmap[i][piece] != NULL)\r
2622           DeleteObject(pieceBitmap[i][piece]);\r
2623       }\r
2624     }\r
2625   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2626   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2627   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2628   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2629   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2630   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2631   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2632   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2633   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2634   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2635   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2636   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2637   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2638   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2639   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2640   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2641   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2642   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2643   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2644   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2645   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2646   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2647   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2648   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2649   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2650   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2651   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2652   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2653   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2654   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2655   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2657   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2658   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2659   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2660   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2661   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2662   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2663   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2664   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2665   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2666   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2667   minorSize = 0;\r
2668   }\r
2669 }\r
2670 \r
2671 HBITMAP\r
2672 PieceBitmap(ChessSquare p, int kind)\r
2673 {\r
2674   if ((int) p >= (int) BlackPawn)\r
2675     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2676 \r
2677   return pieceBitmap[kind][(int) p];\r
2678 }\r
2679 \r
2680 /***************************************************************/\r
2681 \r
2682 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2683 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2684 /*\r
2685 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2686 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2687 */\r
2688 \r
2689 VOID\r
2690 SquareToPos(int row, int column, int * x, int * y)\r
2691 {\r
2692   if (flipView) {\r
2693     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2694     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2695   } else {\r
2696     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2697     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2698   }\r
2699 }\r
2700 \r
2701 VOID\r
2702 DrawCoordsOnDC(HDC hdc)\r
2703 {\r
2704   static char files[] = "0123456789012345678901221098765432109876543210";\r
2705   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2706   char str[2] = { NULLCHAR, NULLCHAR };\r
2707   int oldMode, oldAlign, x, y, start, i;\r
2708   HFONT oldFont;\r
2709   HBRUSH oldBrush;\r
2710 \r
2711   if (!appData.showCoords)\r
2712     return;\r
2713 \r
2714   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2715 \r
2716   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2717   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2718   oldAlign = GetTextAlign(hdc);\r
2719   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2720 \r
2721   y = boardRect.top + lineGap;\r
2722   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2723 \r
2724   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2725   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2726     str[0] = files[start + i];\r
2727     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2728     y += squareSize + lineGap;\r
2729   }\r
2730 \r
2731   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2732 \r
2733   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2734   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2735     str[0] = ranks[start + i];\r
2736     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2737     x += squareSize + lineGap;\r
2738   }    \r
2739 \r
2740   SelectObject(hdc, oldBrush);\r
2741   SetBkMode(hdc, oldMode);\r
2742   SetTextAlign(hdc, oldAlign);\r
2743   SelectObject(hdc, oldFont);\r
2744 }\r
2745 \r
2746 VOID\r
2747 DrawGridOnDC(HDC hdc)\r
2748 {\r
2749   HPEN oldPen;\r
2750  \r
2751   if (lineGap != 0) {\r
2752     oldPen = SelectObject(hdc, gridPen);\r
2753     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2754     SelectObject(hdc, oldPen);\r
2755   }\r
2756 }\r
2757 \r
2758 #define HIGHLIGHT_PEN 0\r
2759 #define PREMOVE_PEN   1\r
2760 \r
2761 VOID\r
2762 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2763 {\r
2764   int x1, y1;\r
2765   HPEN oldPen, hPen;\r
2766   if (lineGap == 0) return;\r
2767   if (flipView) {\r
2768     x1 = boardRect.left +\r
2769       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2770     y1 = boardRect.top +\r
2771       lineGap/2 + y * (squareSize + lineGap);\r
2772   } else {\r
2773     x1 = boardRect.left +\r
2774       lineGap/2 + x * (squareSize + lineGap);\r
2775     y1 = boardRect.top +\r
2776       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2777   }\r
2778   hPen = pen ? premovePen : highlightPen;\r
2779   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2780   MoveToEx(hdc, x1, y1, NULL);\r
2781   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2782   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2783   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2784   LineTo(hdc, x1, y1);\r
2785   SelectObject(hdc, oldPen);\r
2786 }\r
2787 \r
2788 VOID\r
2789 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2790 {\r
2791   int i;\r
2792   for (i=0; i<2; i++) {\r
2793     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2794       DrawHighlightOnDC(hdc, TRUE,\r
2795                         h->sq[i].x, h->sq[i].y,\r
2796                         pen);\r
2797   }\r
2798 }\r
2799 \r
2800 /* Note: sqcolor is used only in monoMode */\r
2801 /* Note that this code is largely duplicated in woptions.c,\r
2802    function DrawSampleSquare, so that needs to be updated too */\r
2803 VOID\r
2804 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2805 {\r
2806   HBITMAP oldBitmap;\r
2807   HBRUSH oldBrush;\r
2808   int tmpSize;\r
2809 \r
2810   if (appData.blindfold) return;\r
2811 \r
2812   /* [AS] Use font-based pieces if needed */\r
2813   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2814     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2815     CreatePiecesFromFont();\r
2816 \r
2817     if( fontBitmapSquareSize == squareSize ) {\r
2818         int index = TranslatePieceToFontPiece(piece);\r
2819 \r
2820         SelectObject( tmphdc, hPieceMask[ index ] );\r
2821 \r
2822       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2823         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2824       else\r
2825         BitBlt( hdc,\r
2826             x, y,\r
2827             squareSize, squareSize,\r
2828             tmphdc,\r
2829             0, 0,\r
2830             SRCAND );\r
2831 \r
2832         SelectObject( tmphdc, hPieceFace[ index ] );\r
2833 \r
2834       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2835         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2836       else\r
2837         BitBlt( hdc,\r
2838             x, y,\r
2839             squareSize, squareSize,\r
2840             tmphdc,\r
2841             0, 0,\r
2842             SRCPAINT );\r
2843 \r
2844         return;\r
2845     }\r
2846   }\r
2847 \r
2848   if (appData.monoMode) {\r
2849     SelectObject(tmphdc, PieceBitmap(piece, \r
2850       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2851     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2852            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2853   } else {\r
2854     tmpSize = squareSize;\r
2855     if(minorSize &&\r
2856         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2857          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2858       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2859       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2860       x += (squareSize - minorSize)>>1;\r
2861       y += squareSize - minorSize - 2;\r
2862       tmpSize = minorSize;\r
2863     }\r
2864     if (color || appData.allWhite ) {\r
2865       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2866       if( color )\r
2867               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2868       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2869       if(appData.upsideDown && color==flipView)\r
2870         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2871       else\r
2872         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2873       /* Use black for outline of white pieces */\r
2874       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2875       if(appData.upsideDown && color==flipView)\r
2876         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2877       else\r
2878         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2879     } else {\r
2880       /* Use square color for details of black pieces */\r
2881       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2882       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2883       if(appData.upsideDown && !flipView)\r
2884         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2885       else\r
2886         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2887     }\r
2888     SelectObject(hdc, oldBrush);\r
2889     SelectObject(tmphdc, oldBitmap);\r
2890   }\r
2891 }\r
2892 \r
2893 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2894 int GetBackTextureMode( int algo )\r
2895 {\r
2896     int result = BACK_TEXTURE_MODE_DISABLED;\r
2897 \r
2898     switch( algo ) \r
2899     {\r
2900         case BACK_TEXTURE_MODE_PLAIN:\r
2901             result = 1; /* Always use identity map */\r
2902             break;\r
2903         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2904             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2905             break;\r
2906     }\r
2907 \r
2908     return result;\r
2909 }\r
2910 \r
2911 /* \r
2912     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2913     to handle redraws cleanly (as random numbers would always be different).\r
2914 */\r
2915 VOID RebuildTextureSquareInfo()\r
2916 {\r
2917     BITMAP bi;\r
2918     int lite_w = 0;\r
2919     int lite_h = 0;\r
2920     int dark_w = 0;\r
2921     int dark_h = 0;\r
2922     int row;\r
2923     int col;\r
2924 \r
2925     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2926 \r
2927     if( liteBackTexture != NULL ) {\r
2928         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2929             lite_w = bi.bmWidth;\r
2930             lite_h = bi.bmHeight;\r
2931         }\r
2932     }\r
2933 \r
2934     if( darkBackTexture != NULL ) {\r
2935         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2936             dark_w = bi.bmWidth;\r
2937             dark_h = bi.bmHeight;\r
2938         }\r
2939     }\r
2940 \r
2941     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2942         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2943             if( (col + row) & 1 ) {\r
2944                 /* Lite square */\r
2945                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2946                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2947                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2948                   else\r
2949                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2950                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2951                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2954                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2955                 }\r
2956             }\r
2957             else {\r
2958                 /* Dark square */\r
2959                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2960                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2961                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2962                   else\r
2963                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2964                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2965                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2966                   else\r
2967                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2968                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2969                 }\r
2970             }\r
2971         }\r
2972     }\r
2973 }\r
2974 \r
2975 /* [AS] Arrow highlighting support */\r
2976 \r
2977 static double A_WIDTH = 5; /* Width of arrow body */\r
2978 \r
2979 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2980 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2981 \r
2982 static double Sqr( double x )\r
2983 {\r
2984     return x*x;\r
2985 }\r
2986 \r
2987 static int Round( double x )\r
2988 {\r
2989     return (int) (x + 0.5);\r
2990 }\r
2991 \r
2992 /* Draw an arrow between two points using current settings */\r
2993 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2994 {\r
2995     POINT arrow[7];\r
2996     double dx, dy, j, k, x, y;\r
2997 \r
2998     if( d_x == s_x ) {\r
2999         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3000 \r
3001         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3002         arrow[0].y = s_y;\r
3003 \r
3004         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3005         arrow[1].y = d_y - h;\r
3006 \r
3007         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[2].y = d_y - h;\r
3009 \r
3010         arrow[3].x = d_x;\r
3011         arrow[3].y = d_y;\r
3012 \r
3013         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3014         arrow[5].y = d_y - h;\r
3015 \r
3016         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3017         arrow[4].y = d_y - h;\r
3018 \r
3019         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3020         arrow[6].y = s_y;\r
3021     }\r
3022     else if( d_y == s_y ) {\r
3023         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3024 \r
3025         arrow[0].x = s_x;\r
3026         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3027 \r
3028         arrow[1].x = d_x - w;\r
3029         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3030 \r
3031         arrow[2].x = d_x - w;\r
3032         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[3].x = d_x;\r
3035         arrow[3].y = d_y;\r
3036 \r
3037         arrow[5].x = d_x - w;\r
3038         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3039 \r
3040         arrow[4].x = d_x - w;\r
3041         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3042 \r
3043         arrow[6].x = s_x;\r
3044         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3045     }\r
3046     else {\r
3047         /* [AS] Needed a lot of paper for this! :-) */\r
3048         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3049         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3050   \r
3051         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3052 \r
3053         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3054 \r
3055         x = s_x;\r
3056         y = s_y;\r
3057 \r
3058         arrow[0].x = Round(x - j);\r
3059         arrow[0].y = Round(y + j*dx);\r
3060 \r
3061         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3062         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3063 \r
3064         if( d_x > s_x ) {\r
3065             x = (double) d_x - k;\r
3066             y = (double) d_y - k*dy;\r
3067         }\r
3068         else {\r
3069             x = (double) d_x + k;\r
3070             y = (double) d_y + k*dy;\r
3071         }\r
3072 \r
3073         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3074 \r
3075         arrow[6].x = Round(x - j);\r
3076         arrow[6].y = Round(y + j*dx);\r
3077 \r
3078         arrow[2].x = Round(arrow[6].x + 2*j);\r
3079         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3080 \r
3081         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3082         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3083 \r
3084         arrow[4].x = d_x;\r
3085         arrow[4].y = d_y;\r
3086 \r
3087         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3088         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3089     }\r
3090 \r
3091     Polygon( hdc, arrow, 7 );\r
3092 }\r
3093 \r
3094 /* [AS] Draw an arrow between two squares */\r
3095 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3096 {\r
3097     int s_x, s_y, d_x, d_y;\r
3098     HPEN hpen;\r
3099     HPEN holdpen;\r
3100     HBRUSH hbrush;\r
3101     HBRUSH holdbrush;\r
3102     LOGBRUSH stLB;\r
3103 \r
3104     if( s_col == d_col && s_row == d_row ) {\r
3105         return;\r
3106     }\r
3107 \r
3108     /* Get source and destination points */\r
3109     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3110     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3111 \r
3112     if( d_y > s_y ) {\r
3113         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3114     }\r
3115     else if( d_y < s_y ) {\r
3116         d_y += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_y += squareSize / 2;\r
3120     }\r
3121 \r
3122     if( d_x > s_x ) {\r
3123         d_x += squareSize / 2 - squareSize / 4;\r
3124     }\r
3125     else if( d_x < s_x ) {\r
3126         d_x += squareSize / 2 + squareSize / 4;\r
3127     }\r
3128     else {\r
3129         d_x += squareSize / 2;\r
3130     }\r
3131 \r
3132     s_x += squareSize / 2;\r
3133     s_y += squareSize / 2;\r
3134 \r
3135     /* Adjust width */\r
3136     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3137 \r
3138     /* Draw */\r
3139     stLB.lbStyle = BS_SOLID;\r
3140     stLB.lbColor = appData.highlightArrowColor;\r
3141     stLB.lbHatch = 0;\r
3142 \r
3143     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3144     holdpen = SelectObject( hdc, hpen );\r
3145     hbrush = CreateBrushIndirect( &stLB );\r
3146     holdbrush = SelectObject( hdc, hbrush );\r
3147 \r
3148     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3149 \r
3150     SelectObject( hdc, holdpen );\r
3151     SelectObject( hdc, holdbrush );\r
3152     DeleteObject( hpen );\r
3153     DeleteObject( hbrush );\r
3154 }\r
3155 \r
3156 BOOL HasHighlightInfo()\r
3157 {\r
3158     BOOL result = FALSE;\r
3159 \r
3160     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3161         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3162     {\r
3163         result = TRUE;\r
3164     }\r
3165 \r
3166     return result;\r
3167 }\r
3168 \r
3169 BOOL IsDrawArrowEnabled()\r
3170 {\r
3171     BOOL result = FALSE;\r
3172 \r
3173     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3174         result = TRUE;\r
3175     }\r
3176 \r
3177     return result;\r
3178 }\r
3179 \r
3180 VOID DrawArrowHighlight( HDC hdc )\r
3181 {\r
3182     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3183         DrawArrowBetweenSquares( hdc,\r
3184             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3185             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3186     }\r
3187 }\r
3188 \r
3189 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3190 {\r
3191     HRGN result = NULL;\r
3192 \r
3193     if( HasHighlightInfo() ) {\r
3194         int x1, y1, x2, y2;\r
3195         int sx, sy, dx, dy;\r
3196 \r
3197         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3198         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3199 \r
3200         sx = MIN( x1, x2 );\r
3201         sy = MIN( y1, y2 );\r
3202         dx = MAX( x1, x2 ) + squareSize;\r
3203         dy = MAX( y1, y2 ) + squareSize;\r
3204 \r
3205         result = CreateRectRgn( sx, sy, dx, dy );\r
3206     }\r
3207 \r
3208     return result;\r
3209 }\r
3210 \r
3211 /*\r
3212     Warning: this function modifies the behavior of several other functions. \r
3213     \r
3214     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3215     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3216     repaint is scattered all over the place, which is not good for features such as\r
3217     "arrow highlighting" that require a full repaint of the board.\r
3218 \r
3219     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3220     user interaction, when speed is not so important) but especially to avoid errors\r
3221     in the displayed graphics.\r
3222 \r
3223     In such patched places, I always try refer to this function so there is a single\r
3224     place to maintain knowledge.\r
3225     \r
3226     To restore the original behavior, just return FALSE unconditionally.\r
3227 */\r
3228 BOOL IsFullRepaintPreferrable()\r
3229 {\r
3230     BOOL result = FALSE;\r
3231 \r
3232     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3233         /* Arrow may appear on the board */\r
3234         result = TRUE;\r
3235     }\r
3236 \r
3237     return result;\r
3238 }\r
3239 \r
3240 /* \r
3241     This function is called by DrawPosition to know whether a full repaint must\r
3242     be forced or not.\r
3243 \r
3244     Only DrawPosition may directly call this function, which makes use of \r
3245     some state information. Other function should call DrawPosition specifying \r
3246     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3247 */\r
3248 BOOL DrawPositionNeedsFullRepaint()\r
3249 {\r
3250     BOOL result = FALSE;\r
3251 \r
3252     /* \r
3253         Probably a slightly better policy would be to trigger a full repaint\r
3254         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3255         but animation is fast enough that it's difficult to notice.\r
3256     */\r
3257     if( animInfo.piece == EmptySquare ) {\r
3258         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3259             result = TRUE;\r
3260         }\r
3261     }\r
3262 \r
3263     return result;\r
3264 }\r
3265 \r
3266 VOID\r
3267 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3268 {\r
3269   int row, column, x, y, square_color, piece_color;\r
3270   ChessSquare piece;\r
3271   HBRUSH oldBrush;\r
3272   HDC texture_hdc = NULL;\r
3273 \r
3274   /* [AS] Initialize background textures if needed */\r
3275   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3276       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3277       if( backTextureSquareSize != squareSize \r
3278        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3279           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3280           backTextureSquareSize = squareSize;\r
3281           RebuildTextureSquareInfo();\r
3282       }\r
3283 \r
3284       texture_hdc = CreateCompatibleDC( hdc );\r
3285   }\r
3286 \r
3287   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3288     for (column = 0; column < BOARD_WIDTH; column++) {\r
3289   \r
3290       SquareToPos(row, column, &x, &y);\r
3291 \r
3292       piece = board[row][column];\r
3293 \r
3294       square_color = ((column + row) % 2) == 1;\r
3295       if( gameInfo.variant == VariantXiangqi ) {\r
3296           square_color = !InPalace(row, column);\r
3297           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3298           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3299       }\r
3300       piece_color = (int) piece < (int) BlackPawn;\r
3301 \r
3302 \r
3303       /* [HGM] holdings file: light square or black */\r
3304       if(column == BOARD_LEFT-2) {\r
3305             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3306                 square_color = 1;\r
3307             else {\r
3308                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3309                 continue;\r
3310             }\r
3311       } else\r
3312       if(column == BOARD_RGHT + 1 ) {\r
3313             if( row < gameInfo.holdingsSize )\r
3314                 square_color = 1;\r
3315             else {\r
3316                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3317                 continue;\r
3318             }\r
3319       }\r
3320       if(column == BOARD_LEFT-1 ) /* left align */\r
3321             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3322       else if( column == BOARD_RGHT) /* right align */\r
3323             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3324       else\r
3325       if (appData.monoMode) {\r
3326         if (piece == EmptySquare) {\r
3327           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3328                  square_color ? WHITENESS : BLACKNESS);\r
3329         } else {\r
3330           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3331         }\r
3332       } \r
3333       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3334           /* [AS] Draw the square using a texture bitmap */\r
3335           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3336           int r = row, c = column; // [HGM] do not flip board in flipView\r
3337           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3338 \r
3339           DrawTile( x, y, \r
3340               squareSize, squareSize, \r
3341               hdc, \r
3342               texture_hdc,\r
3343               backTextureSquareInfo[r][c].mode,\r
3344               backTextureSquareInfo[r][c].x,\r
3345               backTextureSquareInfo[r][c].y );\r
3346 \r
3347           SelectObject( texture_hdc, hbm );\r
3348 \r
3349           if (piece != EmptySquare) {\r
3350               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351           }\r
3352       }\r
3353       else {\r
3354         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3355 \r
3356         oldBrush = SelectObject(hdc, brush );\r
3357         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3358         SelectObject(hdc, oldBrush);\r
3359         if (piece != EmptySquare)\r
3360           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3361       }\r
3362     }\r
3363   }\r
3364 \r
3365   if( texture_hdc != NULL ) {\r
3366     DeleteDC( texture_hdc );\r
3367   }\r
3368 }\r
3369 \r
3370 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3371 void fputDW(FILE *f, int x)\r
3372 {\r
3373         fputc(x     & 255, f);\r
3374         fputc(x>>8  & 255, f);\r
3375         fputc(x>>16 & 255, f);\r
3376         fputc(x>>24 & 255, f);\r
3377 }\r
3378 \r
3379 #define MAX_CLIPS 200   /* more than enough */\r
3380 \r
3381 VOID\r
3382 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3383 {\r
3384 //  HBITMAP bufferBitmap;\r
3385   BITMAP bi;\r
3386 //  RECT Rect;\r
3387   HDC tmphdc;\r
3388   HBITMAP hbm;\r
3389   int w = 100, h = 50;\r
3390 \r
3391   if(logo == NULL) {\r
3392     if(!logoHeight) return;\r
3393     FillRect( hdc, &logoRect, whitePieceBrush );\r
3394   }\r
3395 //  GetClientRect(hwndMain, &Rect);\r
3396 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3397 //                                      Rect.bottom-Rect.top+1);\r
3398   tmphdc = CreateCompatibleDC(hdc);\r
3399   hbm = SelectObject(tmphdc, logo);\r
3400   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3401             w = bi.bmWidth;\r
3402             h = bi.bmHeight;\r
3403   }\r
3404   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3405                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3406   SelectObject(tmphdc, hbm);\r
3407   DeleteDC(tmphdc);\r
3408 }\r
3409 \r
3410 VOID\r
3411 DisplayLogos()\r
3412 {\r
3413   if(logoHeight) {\r
3414         HDC hdc = GetDC(hwndMain);\r
3415         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3416         if(appData.autoLogo) {\r
3417           \r
3418           switch(gameMode) { // pick logos based on game mode\r
3419             case IcsObserving:\r
3420                 whiteLogo = second.programLogo; // ICS logo\r
3421                 blackLogo = second.programLogo;\r
3422             default:\r
3423                 break;\r
3424             case IcsPlayingWhite:\r
3425                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3426                 blackLogo = second.programLogo; // ICS logo\r
3427                 break;\r
3428             case IcsPlayingBlack:\r
3429                 whiteLogo = second.programLogo; // ICS logo\r
3430                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3431                 break;\r
3432             case TwoMachinesPlay:\r
3433                 if(first.twoMachinesColor[0] == 'b') {\r
3434                     whiteLogo = second.programLogo;\r
3435                     blackLogo = first.programLogo;\r
3436                 }\r
3437                 break;\r
3438             case MachinePlaysWhite:\r
3439                 blackLogo = userLogo;\r
3440                 break;\r
3441             case MachinePlaysBlack:\r
3442                 whiteLogo = userLogo;\r
3443                 blackLogo = first.programLogo;\r
3444           }\r
3445         }\r
3446         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3447         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3448         ReleaseDC(hwndMain, hdc);\r
3449   }\r
3450 }\r
3451 \r
3452 void\r
3453 UpdateLogos(int display)\r
3454 { // called after loading new engine(s), in tourney or from menu\r
3455   LoadLogo(&first, 0, FALSE);\r
3456   LoadLogo(&second, 1, appData.icsActive);\r
3457   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3458   if(display) DisplayLogos();\r
3459 }\r
3460 \r
3461 static HDC hdcSeek;\r
3462 \r
3463 // [HGM] seekgraph\r
3464 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3465 {\r
3466     POINT stPt;\r
3467     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3468     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3469     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3470     SelectObject( hdcSeek, hp );\r
3471 }\r
3472 \r
3473 // front-end wrapper for drawing functions to do rectangles\r
3474 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3475 {\r
3476     HPEN hp;\r
3477     RECT rc;\r
3478 \r
3479     if (hdcSeek == NULL) {\r
3480     hdcSeek = GetDC(hwndMain);\r
3481       if (!appData.monoMode) {\r
3482         SelectPalette(hdcSeek, hPal, FALSE);\r
3483         RealizePalette(hdcSeek);\r
3484       }\r
3485     }\r
3486     hp = SelectObject( hdcSeek, gridPen );\r
3487     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3488     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3489     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3490     SelectObject( hdcSeek, hp );\r
3491 }\r
3492 \r
3493 // front-end wrapper for putting text in graph\r
3494 void DrawSeekText(char *buf, int x, int y)\r
3495 {\r
3496         SIZE stSize;\r
3497         SetBkMode( hdcSeek, TRANSPARENT );\r
3498         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3499         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3500 }\r
3501 \r
3502 void DrawSeekDot(int x, int y, int color)\r
3503 {\r
3504         int square = color & 0x80;\r
3505         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3506                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3507         color &= 0x7F;\r
3508         if(square)\r
3509             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3510                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3511         else\r
3512             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3513                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3514             SelectObject(hdcSeek, oldBrush);\r
3515 }\r
3516 \r
3517 VOID\r
3518 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3519 {\r
3520   static Board lastReq[2], lastDrawn[2];\r
3521   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3522   static int lastDrawnFlipView = 0;\r
3523   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3524   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3525   HDC tmphdc;\r
3526   HDC hdcmem;\r
3527   HBITMAP bufferBitmap;\r
3528   HBITMAP oldBitmap;\r
3529   RECT Rect;\r
3530   HRGN clips[MAX_CLIPS];\r
3531   ChessSquare dragged_piece = EmptySquare;\r
3532   int nr = twoBoards*partnerUp;\r
3533 \r
3534   /* I'm undecided on this - this function figures out whether a full\r
3535    * repaint is necessary on its own, so there's no real reason to have the\r
3536    * caller tell it that.  I think this can safely be set to FALSE - but\r
3537    * if we trust the callers not to request full repaints unnessesarily, then\r
3538    * we could skip some clipping work.  In other words, only request a full\r
3539    * redraw when the majority of pieces have changed positions (ie. flip, \r
3540    * gamestart and similar)  --Hawk\r
3541    */\r
3542   Boolean fullrepaint = repaint;\r
3543 \r
3544   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3545 \r
3546   if( DrawPositionNeedsFullRepaint() ) {\r
3547       fullrepaint = TRUE;\r
3548   }\r
3549 \r
3550   if (board == NULL) {\r
3551     if (!lastReqValid[nr]) {\r
3552       return;\r
3553     }\r
3554     board = lastReq[nr];\r
3555   } else {\r
3556     CopyBoard(lastReq[nr], board);\r
3557     lastReqValid[nr] = 1;\r
3558   }\r
3559 \r
3560   if (doingSizing) {\r
3561     return;\r
3562   }\r
3563 \r
3564   if (IsIconic(hwndMain)) {\r
3565     return;\r
3566   }\r
3567 \r
3568   if (hdc == NULL) {\r
3569     hdc = GetDC(hwndMain);\r
3570     if (!appData.monoMode) {\r
3571       SelectPalette(hdc, hPal, FALSE);\r
3572       RealizePalette(hdc);\r
3573     }\r
3574     releaseDC = TRUE;\r
3575   } else {\r
3576     releaseDC = FALSE;\r
3577   }\r
3578 \r
3579   /* Create some work-DCs */\r
3580   hdcmem = CreateCompatibleDC(hdc);\r
3581   tmphdc = CreateCompatibleDC(hdc);\r
3582 \r
3583   /* If dragging is in progress, we temporarely remove the piece */\r
3584   /* [HGM] or temporarily decrease count if stacked              */\r
3585   /*       !! Moved to before board compare !!                   */\r
3586   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3587     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3588     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3589             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3590         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3591     } else \r
3592     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3593             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3594         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3595     } else \r
3596         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3597   }\r
3598 \r
3599   /* Figure out which squares need updating by comparing the \r
3600    * newest board with the last drawn board and checking if\r
3601    * flipping has changed.\r
3602    */\r
3603   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3604     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3605       for (column = 0; column < BOARD_WIDTH; column++) {\r
3606         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3607           SquareToPos(row, column, &x, &y);\r
3608           clips[num_clips++] =\r
3609             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3610         }\r
3611       }\r
3612     }\r
3613    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3614     for (i=0; i<2; i++) {\r
3615       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3616           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3617         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3618             lastDrawnHighlight.sq[i].y >= 0) {\r
3619           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3620                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3621           clips[num_clips++] =\r
3622             CreateRectRgn(x - lineGap, y - lineGap, \r
3623                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3624         }\r
3625         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3626           SquareToPos(highlightInfo.sq[i].y, highlightInfo.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       }\r
3632     }\r
3633     for (i=0; i<2; i++) {\r
3634       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3635           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3636         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3637             lastDrawnPremove.sq[i].y >= 0) {\r
3638           SquareToPos(lastDrawnPremove.sq[i].y,\r
3639                       lastDrawnPremove.sq[i].x, &x, &y);\r
3640           clips[num_clips++] =\r
3641             CreateRectRgn(x - lineGap, y - lineGap, \r
3642                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3643         }\r
3644         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3645             premoveHighlightInfo.sq[i].y >= 0) {\r
3646           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3647                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3648           clips[num_clips++] =\r
3649             CreateRectRgn(x - lineGap, y - lineGap, \r
3650                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3651         }\r
3652       }\r
3653     }\r
3654    } else { // nr == 1\r
3655         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3656         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3657         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3658         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3659       for (i=0; i<2; i++) {\r
3660         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3661             partnerHighlightInfo.sq[i].y >= 0) {\r
3662           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3663                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3664           clips[num_clips++] =\r
3665             CreateRectRgn(x - lineGap, y - lineGap, \r
3666                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3667         }\r
3668         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3669             oldPartnerHighlight.sq[i].y >= 0) {\r
3670           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3671                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3672           clips[num_clips++] =\r
3673             CreateRectRgn(x - lineGap, y - lineGap, \r
3674                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3675         }\r
3676       }\r
3677    }\r
3678   } else {\r
3679     fullrepaint = TRUE;\r
3680   }\r
3681 \r
3682   /* Create a buffer bitmap - this is the actual bitmap\r
3683    * being written to.  When all the work is done, we can\r
3684    * copy it to the real DC (the screen).  This avoids\r
3685    * the problems with flickering.\r
3686    */\r
3687   GetClientRect(hwndMain, &Rect);\r
3688   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3689                                         Rect.bottom-Rect.top+1);\r
3690   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3691   if (!appData.monoMode) {\r
3692     SelectPalette(hdcmem, hPal, FALSE);\r
3693   }\r
3694 \r
3695   /* Create clips for dragging */\r
3696   if (!fullrepaint) {\r
3697     if (dragInfo.from.x >= 0) {\r
3698       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3699       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3700     }\r
3701     if (dragInfo.start.x >= 0) {\r
3702       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3703       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3704     }\r
3705     if (dragInfo.pos.x >= 0) {\r
3706       x = dragInfo.pos.x - squareSize / 2;\r
3707       y = dragInfo.pos.y - squareSize / 2;\r
3708       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3709     }\r
3710     if (dragInfo.lastpos.x >= 0) {\r
3711       x = dragInfo.lastpos.x - squareSize / 2;\r
3712       y = dragInfo.lastpos.y - squareSize / 2;\r
3713       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3714     }\r
3715   }\r
3716 \r
3717   /* Are we animating a move?  \r
3718    * If so, \r
3719    *   - remove the piece from the board (temporarely)\r
3720    *   - calculate the clipping region\r
3721    */\r
3722   if (!fullrepaint) {\r
3723     if (animInfo.piece != EmptySquare) {\r
3724       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3725       x = boardRect.left + animInfo.lastpos.x;\r
3726       y = boardRect.top + animInfo.lastpos.y;\r
3727       x2 = boardRect.left + animInfo.pos.x;\r
3728       y2 = boardRect.top + animInfo.pos.y;\r
3729       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3730       /* Slight kludge.  The real problem is that after AnimateMove is\r
3731          done, the position on the screen does not match lastDrawn.\r
3732          This currently causes trouble only on e.p. captures in\r
3733          atomic, where the piece moves to an empty square and then\r
3734          explodes.  The old and new positions both had an empty square\r
3735          at the destination, but animation has drawn a piece there and\r
3736          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3737       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3738     }\r
3739   }\r
3740 \r
3741   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3742   if (num_clips == 0)\r
3743     fullrepaint = TRUE;\r
3744 \r
3745   /* Set clipping on the memory DC */\r
3746   if (!fullrepaint) {\r
3747     SelectClipRgn(hdcmem, clips[0]);\r
3748     for (x = 1; x < num_clips; x++) {\r
3749       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3750         abort();  // this should never ever happen!\r
3751     }\r
3752   }\r
3753 \r
3754   /* Do all the drawing to the memory DC */\r
3755   if(explodeInfo.radius) { // [HGM] atomic\r
3756         HBRUSH oldBrush;\r
3757         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3758         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3759         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3760         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3761         x += squareSize/2;\r
3762         y += squareSize/2;\r
3763         if(!fullrepaint) {\r
3764           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3765           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3766         }\r
3767         DrawGridOnDC(hdcmem);\r
3768         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3769         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3770         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3771         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3772         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3773         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3774         SelectObject(hdcmem, oldBrush);\r
3775   } else {\r
3776     DrawGridOnDC(hdcmem);\r
3777     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3778         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3779         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3780     } else {\r
3781         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3782         oldPartnerHighlight = partnerHighlightInfo;\r
3783     }\r
3784     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3785   }\r
3786   if(nr == 0) // [HGM] dual: markers only on left board\r
3787   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3788     for (column = 0; column < BOARD_WIDTH; column++) {\r
3789         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3790             HBRUSH oldBrush = SelectObject(hdcmem, \r
3791                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3792             SquareToPos(row, column, &x, &y);\r
3793             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3794                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3795             SelectObject(hdcmem, oldBrush);\r
3796         }\r
3797     }\r
3798   }\r
3799 \r
3800   if( appData.highlightMoveWithArrow ) {\r
3801     DrawArrowHighlight(hdcmem);\r
3802   }\r
3803 \r
3804   DrawCoordsOnDC(hdcmem);\r
3805 \r
3806   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3807                  /* to make sure lastDrawn contains what is actually drawn */\r
3808 \r
3809   /* Put the dragged piece back into place and draw it (out of place!) */\r
3810     if (dragged_piece != EmptySquare) {\r
3811     /* [HGM] or restack */\r
3812     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3813                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3814     else\r
3815     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3816                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3817     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3818     x = dragInfo.pos.x - squareSize / 2;\r
3819     y = dragInfo.pos.y - squareSize / 2;\r
3820     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3821                   ((int) dragInfo.piece < (int) BlackPawn), \r
3822                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3823   }   \r
3824   \r
3825   /* Put the animated piece back into place and draw it */\r
3826   if (animInfo.piece != EmptySquare) {\r
3827     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3828     x = boardRect.left + animInfo.pos.x;\r
3829     y = boardRect.top + animInfo.pos.y;\r
3830     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3831                   ((int) animInfo.piece < (int) BlackPawn),\r
3832                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3833   }\r
3834 \r
3835   /* Release the bufferBitmap by selecting in the old bitmap \r
3836    * and delete the memory DC\r
3837    */\r
3838   SelectObject(hdcmem, oldBitmap);\r
3839   DeleteDC(hdcmem);\r
3840 \r
3841   /* Set clipping on the target DC */\r
3842   if (!fullrepaint) {\r
3843     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3844         RECT rect;\r
3845         GetRgnBox(clips[x], &rect);\r
3846         DeleteObject(clips[x]);\r
3847         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3848                           rect.right + wpMain.width/2, rect.bottom);\r
3849     }\r
3850     SelectClipRgn(hdc, clips[0]);\r
3851     for (x = 1; x < num_clips; x++) {\r
3852       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3853         abort();   // this should never ever happen!\r
3854     } \r
3855   }\r
3856 \r
3857   /* Copy the new bitmap onto the screen in one go.\r
3858    * This way we avoid any flickering\r
3859    */\r
3860   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3861   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3862          boardRect.right - boardRect.left,\r
3863          boardRect.bottom - boardRect.top,\r
3864          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3865   if(saveDiagFlag) { \r
3866     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3867     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3868 \r
3869     GetObject(bufferBitmap, sizeof(b), &b);\r
3870     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3871         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3872         bih.biWidth = b.bmWidth;\r
3873         bih.biHeight = b.bmHeight;\r
3874         bih.biPlanes = 1;\r
3875         bih.biBitCount = b.bmBitsPixel;\r
3876         bih.biCompression = 0;\r
3877         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3878         bih.biXPelsPerMeter = 0;\r
3879         bih.biYPelsPerMeter = 0;\r
3880         bih.biClrUsed = 0;\r
3881         bih.biClrImportant = 0;\r
3882 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3883 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3884         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3885 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3886 \r
3887         wb = b.bmWidthBytes;\r
3888         // count colors\r
3889         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3890                 int k = ((int*) pData)[i];\r
3891                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3892                 if(j >= 16) break;\r
3893                 color[j] = k;\r
3894                 if(j >= nrColors) nrColors = j+1;\r
3895         }\r
3896         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3897                 INT p = 0;\r
3898                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3899                     for(w=0; w<(wb>>2); w+=2) {\r
3900                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3901                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3902                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3903                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3904                         pData[p++] = m | j<<4;\r
3905                     }\r
3906                     while(p&3) pData[p++] = 0;\r
3907                 }\r
3908                 fac = 3;\r
3909                 wb = ((wb+31)>>5)<<2;\r
3910         }\r
3911         // write BITMAPFILEHEADER\r
3912         fprintf(diagFile, "BM");\r
3913         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3914         fputDW(diagFile, 0);\r
3915         fputDW(diagFile, 0x36 + (fac?64:0));\r
3916         // write BITMAPINFOHEADER\r
3917         fputDW(diagFile, 40);\r
3918         fputDW(diagFile, b.bmWidth);\r
3919         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3920         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3921         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3922         fputDW(diagFile, 0);\r
3923         fputDW(diagFile, 0);\r
3924         fputDW(diagFile, 0);\r
3925         fputDW(diagFile, 0);\r
3926         fputDW(diagFile, 0);\r
3927         fputDW(diagFile, 0);\r
3928         // write color table\r
3929         if(fac)\r
3930         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3931         // write bitmap data\r
3932         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3933                 fputc(pData[i], diagFile);\r
3934         free(pData);\r
3935      }\r
3936   }\r
3937 \r
3938   SelectObject(tmphdc, oldBitmap);\r
3939 \r
3940   /* Massive cleanup */\r
3941   for (x = 0; x < num_clips; x++)\r
3942     DeleteObject(clips[x]);\r
3943 \r
3944   DeleteDC(tmphdc);\r
3945   DeleteObject(bufferBitmap);\r
3946 \r
3947   if (releaseDC) \r
3948     ReleaseDC(hwndMain, hdc);\r
3949   \r
3950   if (lastDrawnFlipView != flipView && nr == 0) {\r
3951     if (flipView)\r
3952       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3953     else\r
3954       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3955   }\r
3956 \r
3957 /*  CopyBoard(lastDrawn, board);*/\r
3958   lastDrawnHighlight = highlightInfo;\r
3959   lastDrawnPremove   = premoveHighlightInfo;\r
3960   lastDrawnFlipView = flipView;\r
3961   lastDrawnValid[nr] = 1;\r
3962 }\r
3963 \r
3964 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3965 int\r
3966 SaveDiagram(f)\r
3967      FILE *f;\r
3968 {\r
3969     saveDiagFlag = 1; diagFile = f;\r
3970     HDCDrawPosition(NULL, TRUE, NULL);\r
3971     saveDiagFlag = 0;\r
3972 \r
3973     fclose(f);\r
3974     return TRUE;\r
3975 }\r
3976 \r
3977 \r
3978 /*---------------------------------------------------------------------------*\\r
3979 | CLIENT PAINT PROCEDURE\r
3980 |   This is the main event-handler for the WM_PAINT message.\r
3981 |\r
3982 \*---------------------------------------------------------------------------*/\r
3983 VOID\r
3984 PaintProc(HWND hwnd)\r
3985 {\r
3986   HDC         hdc;\r
3987   PAINTSTRUCT ps;\r
3988   HFONT       oldFont;\r
3989 \r
3990   if((hdc = BeginPaint(hwnd, &ps))) {\r
3991     if (IsIconic(hwnd)) {\r
3992       DrawIcon(hdc, 2, 2, iconCurrent);\r
3993     } else {\r
3994       if (!appData.monoMode) {\r
3995         SelectPalette(hdc, hPal, FALSE);\r
3996         RealizePalette(hdc);\r
3997       }\r
3998       HDCDrawPosition(hdc, 1, NULL);\r
3999       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4000         flipView = !flipView; partnerUp = !partnerUp;\r
4001         HDCDrawPosition(hdc, 1, NULL);\r
4002         flipView = !flipView; partnerUp = !partnerUp;\r
4003       }\r
4004       oldFont =\r
4005         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4006       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4007                  ETO_CLIPPED|ETO_OPAQUE,\r
4008                  &messageRect, messageText, strlen(messageText), NULL);\r
4009       SelectObject(hdc, oldFont);\r
4010       DisplayBothClocks();\r
4011       DisplayLogos();\r
4012     }\r
4013     EndPaint(hwnd,&ps);\r
4014   }\r
4015 \r
4016   return;\r
4017 }\r
4018 \r
4019 \r
4020 /*\r
4021  * If the user selects on a border boundary, return -1; if off the board,\r
4022  *   return -2.  Otherwise map the event coordinate to the square.\r
4023  * The offset boardRect.left or boardRect.top must already have been\r
4024  *   subtracted from x.\r
4025  */\r
4026 int EventToSquare(x, limit)\r
4027      int x, limit;\r
4028 {\r
4029   if (x <= 0)\r
4030     return -2;\r
4031   if (x < lineGap)\r
4032     return -1;\r
4033   x -= lineGap;\r
4034   if ((x % (squareSize + lineGap)) >= squareSize)\r
4035     return -1;\r
4036   x /= (squareSize + lineGap);\r
4037     if (x >= limit)\r
4038     return -2;\r
4039   return x;\r
4040 }\r
4041 \r
4042 typedef struct {\r
4043   char piece;\r
4044   int command;\r
4045   char* name;\r
4046 } DropEnable;\r
4047 \r
4048 DropEnable dropEnables[] = {\r
4049   { 'P', DP_Pawn, N_("Pawn") },\r
4050   { 'N', DP_Knight, N_("Knight") },\r
4051   { 'B', DP_Bishop, N_("Bishop") },\r
4052   { 'R', DP_Rook, N_("Rook") },\r
4053   { 'Q', DP_Queen, N_("Queen") },\r
4054 };\r
4055 \r
4056 VOID\r
4057 SetupDropMenu(HMENU hmenu)\r
4058 {\r
4059   int i, count, enable;\r
4060   char *p;\r
4061   extern char white_holding[], black_holding[];\r
4062   char item[MSG_SIZ];\r
4063 \r
4064   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4065     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4066                dropEnables[i].piece);\r
4067     count = 0;\r
4068     while (p && *p++ == dropEnables[i].piece) count++;\r
4069       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4070     enable = count > 0 || !appData.testLegality\r
4071       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4072                       && !appData.icsActive);\r
4073     ModifyMenu(hmenu, dropEnables[i].command,\r
4074                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4075                dropEnables[i].command, item);\r
4076   }\r
4077 }\r
4078 \r
4079 void DragPieceBegin(int x, int y, Boolean instantly)\r
4080 {\r
4081       dragInfo.lastpos.x = boardRect.left + x;\r
4082       dragInfo.lastpos.y = boardRect.top + y;\r
4083       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4084       dragInfo.from.x = fromX;\r
4085       dragInfo.from.y = fromY;\r
4086       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4087       dragInfo.start = dragInfo.from;\r
4088       SetCapture(hwndMain);\r
4089 }\r
4090 \r
4091 void DragPieceEnd(int x, int y)\r
4092 {\r
4093     ReleaseCapture();\r
4094     dragInfo.start.x = dragInfo.start.y = -1;\r
4095     dragInfo.from = dragInfo.start;\r
4096     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4097 }\r
4098 \r
4099 void ChangeDragPiece(ChessSquare piece)\r
4100 {\r
4101     dragInfo.piece = piece;\r
4102 }\r
4103 \r
4104 /* Event handler for mouse messages */\r
4105 VOID\r
4106 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4107 {\r
4108   int x, y, menuNr;\r
4109   POINT pt;\r
4110   static int recursive = 0;\r
4111   HMENU hmenu;\r
4112   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4113 \r
4114   if (recursive) {\r
4115     if (message == WM_MBUTTONUP) {\r
4116       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4117          to the middle button: we simulate pressing the left button too!\r
4118          */\r
4119       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4120       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4121     }\r
4122     return;\r
4123   }\r
4124   recursive++;\r
4125   \r
4126   pt.x = LOWORD(lParam);\r
4127   pt.y = HIWORD(lParam);\r
4128   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4129   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4130   if (!flipView && y >= 0) {\r
4131     y = BOARD_HEIGHT - 1 - y;\r
4132   }\r
4133   if (flipView && x >= 0) {\r
4134     x = BOARD_WIDTH - 1 - x;\r
4135   }\r
4136 \r
4137   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4138 \r
4139   switch (message) {\r
4140   case WM_LBUTTONDOWN:\r
4141       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4142         ClockClick(flipClock);\r
4143       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4144         ClockClick(!flipClock);\r
4145       }\r
4146       dragInfo.start.x = dragInfo.start.y = -1;\r
4147       dragInfo.from = dragInfo.start;\r
4148     if(fromX == -1 && frozen) { // not sure where this is for\r
4149                 fromX = fromY = -1; \r
4150       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4151       break;\r
4152     }\r
4153       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4154       DrawPosition(TRUE, NULL);\r
4155     break;\r
4156 \r
4157   case WM_LBUTTONUP:\r
4158       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4159       DrawPosition(TRUE, NULL);\r
4160     break;\r
4161 \r
4162   case WM_MOUSEMOVE:\r
4163     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4164     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4165     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4166     if ((appData.animateDragging || appData.highlightDragging)\r
4167         && (wParam & MK_LBUTTON)\r
4168         && dragInfo.from.x >= 0) \r
4169     {\r
4170       BOOL full_repaint = FALSE;\r
4171 \r
4172       if (appData.animateDragging) {\r
4173         dragInfo.pos = pt;\r
4174       }\r
4175       if (appData.highlightDragging) {\r
4176         SetHighlights(fromX, fromY, x, y);\r
4177         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4178             full_repaint = TRUE;\r
4179         }\r
4180       }\r
4181       \r
4182       DrawPosition( full_repaint, NULL);\r
4183       \r
4184       dragInfo.lastpos = dragInfo.pos;\r
4185     }\r
4186     break;\r
4187 \r
4188   case WM_MOUSEWHEEL: // [DM]\r
4189     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4190        /* Mouse Wheel is being rolled forward\r
4191         * Play moves forward\r
4192         */\r
4193        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4194                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4195        /* Mouse Wheel is being rolled backward\r
4196         * Play moves backward\r
4197         */\r
4198        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4199                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4200     }\r
4201     break;\r
4202 \r
4203   case WM_MBUTTONUP:\r
4204   case WM_RBUTTONUP:\r
4205     ReleaseCapture();\r
4206     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4207     break;\r
4208  \r
4209   case WM_MBUTTONDOWN:\r
4210   case WM_RBUTTONDOWN:\r
4211     ErrorPopDown();\r
4212     ReleaseCapture();\r
4213     fromX = fromY = -1;\r
4214     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4215     dragInfo.start.x = dragInfo.start.y = -1;\r
4216     dragInfo.from = dragInfo.start;\r
4217     dragInfo.lastpos = dragInfo.pos;\r
4218     if (appData.highlightDragging) {\r
4219       ClearHighlights();\r
4220     }\r
4221     if(y == -2) {\r
4222       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4223       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4224           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4225       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4226           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4227       }\r
4228       break;\r
4229     }\r
4230     DrawPosition(TRUE, NULL);\r
4231 \r
4232     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4233     switch (menuNr) {\r
4234     case 0:\r
4235       if (message == WM_MBUTTONDOWN) {\r
4236         buttonCount = 3;  /* even if system didn't think so */\r
4237         if (wParam & MK_SHIFT) \r
4238           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4239         else\r
4240           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4241       } else { /* message == WM_RBUTTONDOWN */\r
4242         /* Just have one menu, on the right button.  Windows users don't\r
4243            think to try the middle one, and sometimes other software steals\r
4244            it, or it doesn't really exist. */\r
4245         if(gameInfo.variant != VariantShogi)\r
4246             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4247         else\r
4248             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4249       }\r
4250       break;\r
4251     case 2:\r
4252       SetCapture(hwndMain);
4253       break;\r
4254     case 1:\r
4255       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4256       SetupDropMenu(hmenu);\r
4257       MenuPopup(hwnd, pt, hmenu, -1);\r
4258     default:\r
4259       break;\r
4260     }\r
4261     break;\r
4262   }\r
4263 \r
4264   recursive--;\r
4265 }\r
4266 \r
4267 /* Preprocess messages for buttons in main window */\r
4268 LRESULT CALLBACK\r
4269 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4270 {\r
4271   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4272   int i, dir;\r
4273 \r
4274   for (i=0; i<N_BUTTONS; i++) {\r
4275     if (buttonDesc[i].id == id) break;\r
4276   }\r
4277   if (i == N_BUTTONS) return 0;\r
4278   switch (message) {\r
4279   case WM_KEYDOWN:\r
4280     switch (wParam) {\r
4281     case VK_LEFT:\r
4282     case VK_RIGHT:\r
4283       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4284       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4285       return TRUE;\r
4286     }\r
4287     break;\r
4288   case WM_CHAR:\r
4289     switch (wParam) {\r
4290     case '\r':\r
4291       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4292       return TRUE;\r
4293     default:\r
4294       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4295         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4296         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4297         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4298         SetFocus(h);\r
4299         SendMessage(h, WM_CHAR, wParam, lParam);\r
4300         return TRUE;\r
4301       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4302         TypeInEvent((char)wParam);\r
4303       }\r
4304       break;\r
4305     }\r
4306     break;\r
4307   }\r
4308   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4309 }\r
4310 \r
4311 /* Process messages for Promotion dialog box */\r
4312 LRESULT CALLBACK\r
4313 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4314 {\r
4315   char promoChar;\r
4316 \r
4317   switch (message) {\r
4318   case WM_INITDIALOG: /* message: initialize dialog box */\r
4319     /* Center the dialog over the application window */\r
4320     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4321     Translate(hDlg, DLG_PromotionKing);\r
4322     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4323       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4324        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4325        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4326                SW_SHOW : SW_HIDE);\r
4327     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4328     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4329        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4330          PieceToChar(WhiteAngel) != '~') ||\r
4331         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4332          PieceToChar(BlackAngel) != '~')   ) ?\r
4333                SW_SHOW : SW_HIDE);\r
4334     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4335        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4336          PieceToChar(WhiteMarshall) != '~') ||\r
4337         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4338          PieceToChar(BlackMarshall) != '~')   ) ?\r
4339                SW_SHOW : SW_HIDE);\r
4340     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4341     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4342        gameInfo.variant != VariantShogi ?\r
4343                SW_SHOW : SW_HIDE);\r
4344     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4345        gameInfo.variant != VariantShogi ?\r
4346                SW_SHOW : SW_HIDE);\r
4347     if(gameInfo.variant == VariantShogi) {\r
4348         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4349         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4350         SetWindowText(hDlg, "Promote?");\r
4351     }\r
4352     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4353        gameInfo.variant == VariantSuper ?\r
4354                SW_SHOW : SW_HIDE);\r
4355     return TRUE;\r
4356 \r
4357   case WM_COMMAND: /* message: received a command */\r
4358     switch (LOWORD(wParam)) {\r
4359     case IDCANCEL:\r
4360       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4361       ClearHighlights();\r
4362       DrawPosition(FALSE, NULL);\r
4363       return TRUE;\r
4364     case PB_King:\r
4365       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4366       break;\r
4367     case PB_Queen:\r
4368       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4369       break;\r
4370     case PB_Rook:\r
4371       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4372       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4373       break;\r
4374     case PB_Bishop:\r
4375       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4376       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4377       break;\r
4378     case PB_Chancellor:\r
4379       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4380       break;\r
4381     case PB_Archbishop:\r
4382       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4383       break;\r
4384     case PB_Knight:\r
4385       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4386       break;\r
4387     default:\r
4388       return FALSE;\r
4389     }\r
4390     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4391     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4392     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4393     fromX = fromY = -1;\r
4394     if (!appData.highlightLastMove) {\r
4395       ClearHighlights();\r
4396       DrawPosition(FALSE, NULL);\r
4397     }\r
4398     return TRUE;\r
4399   }\r
4400   return FALSE;\r
4401 }\r
4402 \r
4403 /* Pop up promotion dialog */\r
4404 VOID\r
4405 PromotionPopup(HWND hwnd)\r
4406 {\r
4407   FARPROC lpProc;\r
4408 \r
4409   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4410   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4411     hwnd, (DLGPROC)lpProc);\r
4412   FreeProcInstance(lpProc);\r
4413 }\r
4414 \r
4415 void\r
4416 PromotionPopUp()\r
4417 {\r
4418   DrawPosition(TRUE, NULL);\r
4419   PromotionPopup(hwndMain);\r
4420 }\r
4421 \r
4422 /* Toggle ShowThinking */\r
4423 VOID\r
4424 ToggleShowThinking()\r
4425 {\r
4426   appData.showThinking = !appData.showThinking;\r
4427   ShowThinkingEvent();\r
4428 }\r
4429 \r
4430 VOID\r
4431 LoadGameDialog(HWND hwnd, char* title)\r
4432 {\r
4433   UINT number = 0;\r
4434   FILE *f;\r
4435   char fileTitle[MSG_SIZ];\r
4436   f = OpenFileDialog(hwnd, "rb", "",\r
4437                      appData.oldSaveStyle ? "gam" : "pgn",\r
4438                      GAME_FILT,\r
4439                      title, &number, fileTitle, NULL);\r
4440   if (f != NULL) {\r
4441     cmailMsgLoaded = FALSE;\r
4442     if (number == 0) {\r
4443       int error = GameListBuild(f);\r
4444       if (error) {\r
4445         DisplayError(_("Cannot build game list"), error);\r
4446       } else if (!ListEmpty(&gameList) &&\r
4447                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4448         GameListPopUp(f, fileTitle);\r
4449         return;\r
4450       }\r
4451       GameListDestroy();\r
4452       number = 1;\r
4453     }\r
4454     LoadGame(f, number, fileTitle, FALSE);\r
4455   }\r
4456 }\r
4457 \r
4458 int get_term_width()\r
4459 {\r
4460     HDC hdc;\r
4461     TEXTMETRIC tm;\r
4462     RECT rc;\r
4463     HFONT hfont, hold_font;\r
4464     LOGFONT lf;\r
4465     HWND hText;\r
4466 \r
4467     if (hwndConsole)\r
4468         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4469     else\r
4470         return 79;\r
4471 \r
4472     // get the text metrics\r
4473     hdc = GetDC(hText);\r
4474     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4475     if (consoleCF.dwEffects & CFE_BOLD)\r
4476         lf.lfWeight = FW_BOLD;\r
4477     if (consoleCF.dwEffects & CFE_ITALIC)\r
4478         lf.lfItalic = TRUE;\r
4479     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4480         lf.lfStrikeOut = TRUE;\r
4481     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4482         lf.lfUnderline = TRUE;\r
4483     hfont = CreateFontIndirect(&lf);\r
4484     hold_font = SelectObject(hdc, hfont);\r
4485     GetTextMetrics(hdc, &tm);\r
4486     SelectObject(hdc, hold_font);\r
4487     DeleteObject(hfont);\r
4488     ReleaseDC(hText, hdc);\r
4489 \r
4490     // get the rectangle\r
4491     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4492 \r
4493     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4494 }\r
4495 \r
4496 void UpdateICSWidth(HWND hText)\r
4497 {\r
4498     LONG old_width, new_width;\r
4499 \r
4500     new_width = get_term_width(hText, FALSE);\r
4501     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4502     if (new_width != old_width)\r
4503     {\r
4504         ics_update_width(new_width);\r
4505         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4506     }\r
4507 }\r
4508 \r
4509 VOID\r
4510 ChangedConsoleFont()\r
4511 {\r
4512   CHARFORMAT cfmt;\r
4513   CHARRANGE tmpsel, sel;\r
4514   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4515   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4516   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4517   PARAFORMAT paraf;\r
4518 \r
4519   cfmt.cbSize = sizeof(CHARFORMAT);\r
4520   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4521     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4522                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4523   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4524    * size.  This was undocumented in the version of MSVC++ that I had\r
4525    * when I wrote the code, but is apparently documented now.\r
4526    */\r
4527   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4528   cfmt.bCharSet = f->lf.lfCharSet;\r
4529   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4530   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4531   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4532   /* Why are the following seemingly needed too? */\r
4533   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4534   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4535   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4536   tmpsel.cpMin = 0;\r
4537   tmpsel.cpMax = -1; /*999999?*/\r
4538   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4539   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4540   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4541    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4542    */\r
4543   paraf.cbSize = sizeof(paraf);\r
4544   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4545   paraf.dxStartIndent = 0;\r
4546   paraf.dxOffset = WRAP_INDENT;\r
4547   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4548   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4549   UpdateICSWidth(hText);\r
4550 }\r
4551 \r
4552 /*---------------------------------------------------------------------------*\\r
4553  *\r
4554  * Window Proc for main window\r
4555  *\r
4556 \*---------------------------------------------------------------------------*/\r
4557 \r
4558 /* Process messages for main window, etc. */\r
4559 LRESULT CALLBACK\r
4560 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4561 {\r
4562   FARPROC lpProc;\r
4563   int wmId, wmEvent;\r
4564   char *defName;\r
4565   FILE *f;\r
4566   UINT number;\r
4567   char fileTitle[MSG_SIZ];\r
4568   char buf[MSG_SIZ];\r
4569   static SnapData sd;\r
4570 \r
4571   switch (message) {\r
4572 \r
4573   case WM_PAINT: /* message: repaint portion of window */\r
4574     PaintProc(hwnd);\r
4575     break;\r
4576 \r
4577   case WM_ERASEBKGND:\r
4578     if (IsIconic(hwnd)) {\r
4579       /* Cheat; change the message */\r
4580       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4581     } else {\r
4582       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4583     }\r
4584     break;\r
4585 \r
4586   case WM_LBUTTONDOWN:\r
4587   case WM_MBUTTONDOWN:\r
4588   case WM_RBUTTONDOWN:\r
4589   case WM_LBUTTONUP:\r
4590   case WM_MBUTTONUP:\r
4591   case WM_RBUTTONUP:\r
4592   case WM_MOUSEMOVE:\r
4593   case WM_MOUSEWHEEL:\r
4594     MouseEvent(hwnd, message, wParam, lParam);\r
4595     break;\r
4596 \r
4597   JAWS_KB_NAVIGATION\r
4598 \r
4599   case WM_CHAR:\r
4600     \r
4601     JAWS_ALT_INTERCEPT\r
4602 \r
4603     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4604         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4605         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4606         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4607         SetFocus(h);\r
4608         SendMessage(h, message, wParam, lParam);\r
4609     } else if(lParam != KF_REPEAT) {\r
4610         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4611                 TypeInEvent((char)wParam);\r
4612         } else if((char)wParam == 003) CopyGameToClipboard();\r
4613          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4614     }\r
4615 \r
4616     break;\r
4617 \r
4618   case WM_PALETTECHANGED:\r
4619     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4620       int nnew;\r
4621       HDC hdc = GetDC(hwndMain);\r
4622       SelectPalette(hdc, hPal, TRUE);\r
4623       nnew = RealizePalette(hdc);\r
4624       if (nnew > 0) {\r
4625         paletteChanged = TRUE;\r
4626         InvalidateRect(hwnd, &boardRect, FALSE);\r
4627       }\r
4628       ReleaseDC(hwnd, hdc);\r
4629     }\r
4630     break;\r
4631 \r
4632   case WM_QUERYNEWPALETTE:\r
4633     if (!appData.monoMode /*&& paletteChanged*/) {\r
4634       int nnew;\r
4635       HDC hdc = GetDC(hwndMain);\r
4636       paletteChanged = FALSE;\r
4637       SelectPalette(hdc, hPal, FALSE);\r
4638       nnew = RealizePalette(hdc);\r
4639       if (nnew > 0) {\r
4640         InvalidateRect(hwnd, &boardRect, FALSE);\r
4641       }\r
4642       ReleaseDC(hwnd, hdc);\r
4643       return TRUE;\r
4644     }\r
4645     return FALSE;\r
4646 \r
4647   case WM_COMMAND: /* message: command from application menu */\r
4648     wmId    = LOWORD(wParam);\r
4649     wmEvent = HIWORD(wParam);\r
4650 \r
4651     switch (wmId) {\r
4652     case IDM_NewGame:\r
4653       ResetGameEvent();\r
4654       SAY("new game enter a move to play against the computer with white");\r
4655       break;\r
4656 \r
4657     case IDM_NewGameFRC:\r
4658       if( NewGameFRC() == 0 ) {\r
4659         ResetGameEvent();\r
4660       }\r
4661       break;\r
4662 \r
4663     case IDM_NewVariant:\r
4664       NewVariantPopup(hwnd);\r
4665       break;\r
4666 \r
4667     case IDM_LoadGame:\r
4668       LoadGameDialog(hwnd, _("Load Game from File"));\r
4669       break;\r
4670 \r
4671     case IDM_LoadNextGame:\r
4672       ReloadGame(1);\r
4673       break;\r
4674 \r
4675     case IDM_LoadPrevGame:\r
4676       ReloadGame(-1);\r
4677       break;\r
4678 \r
4679     case IDM_ReloadGame:\r
4680       ReloadGame(0);\r
4681       break;\r
4682 \r
4683     case IDM_LoadPosition:\r
4684       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4685         Reset(FALSE, TRUE);\r
4686       }\r
4687       number = 1;\r
4688       f = OpenFileDialog(hwnd, "rb", "",\r
4689                          appData.oldSaveStyle ? "pos" : "fen",\r
4690                          POSITION_FILT,\r
4691                          _("Load Position from File"), &number, fileTitle, NULL);\r
4692       if (f != NULL) {\r
4693         LoadPosition(f, number, fileTitle);\r
4694       }\r
4695       break;\r
4696 \r
4697     case IDM_LoadNextPosition:\r
4698       ReloadPosition(1);\r
4699       break;\r
4700 \r
4701     case IDM_LoadPrevPosition:\r
4702       ReloadPosition(-1);\r
4703       break;\r
4704 \r
4705     case IDM_ReloadPosition:\r
4706       ReloadPosition(0);\r
4707       break;\r
4708 \r
4709     case IDM_SaveGame:\r
4710       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4711       f = OpenFileDialog(hwnd, "a", defName,\r
4712                          appData.oldSaveStyle ? "gam" : "pgn",\r
4713                          GAME_FILT,\r
4714                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4715       if (f != NULL) {\r
4716         SaveGame(f, 0, "");\r
4717       }\r
4718       break;\r
4719 \r
4720     case IDM_SavePosition:\r
4721       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4722       f = OpenFileDialog(hwnd, "a", defName,\r
4723                          appData.oldSaveStyle ? "pos" : "fen",\r
4724                          POSITION_FILT,\r
4725                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4726       if (f != NULL) {\r
4727         SavePosition(f, 0, "");\r
4728       }\r
4729       break;\r
4730 \r
4731     case IDM_SaveDiagram:\r
4732       defName = "diagram";\r
4733       f = OpenFileDialog(hwnd, "wb", defName,\r
4734                          "bmp",\r
4735                          DIAGRAM_FILT,\r
4736                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4737       if (f != NULL) {\r
4738         SaveDiagram(f);\r
4739       }\r
4740       break;\r
4741 \r
4742     case IDM_CopyGame:\r
4743       CopyGameToClipboard();\r
4744       break;\r
4745 \r
4746     case IDM_PasteGame:\r
4747       PasteGameFromClipboard();\r
4748       break;\r
4749 \r
4750     case IDM_CopyGameListToClipboard:\r
4751       CopyGameListToClipboard();\r
4752       break;\r
4753 \r
4754     /* [AS] Autodetect FEN or PGN data */\r
4755     case IDM_PasteAny:\r
4756       PasteGameOrFENFromClipboard();\r
4757       break;\r
4758 \r
4759     /* [AS] Move history */\r
4760     case IDM_ShowMoveHistory:\r
4761         if( MoveHistoryIsUp() ) {\r
4762             MoveHistoryPopDown();\r
4763         }\r
4764         else {\r
4765             MoveHistoryPopUp();\r
4766         }\r
4767         break;\r
4768 \r
4769     /* [AS] Eval graph */\r
4770     case IDM_ShowEvalGraph:\r
4771         if( EvalGraphIsUp() ) {\r
4772             EvalGraphPopDown();\r
4773         }\r
4774         else {\r
4775             EvalGraphPopUp();\r
4776             SetFocus(hwndMain);\r
4777         }\r
4778         break;\r
4779 \r
4780     /* [AS] Engine output */\r
4781     case IDM_ShowEngineOutput:\r
4782         if( EngineOutputIsUp() ) {\r
4783             EngineOutputPopDown();\r
4784         }\r
4785         else {\r
4786             EngineOutputPopUp();\r
4787         }\r
4788         break;\r
4789 \r
4790     /* [AS] User adjudication */\r
4791     case IDM_UserAdjudication_White:\r
4792         UserAdjudicationEvent( +1 );\r
4793         break;\r
4794 \r
4795     case IDM_UserAdjudication_Black:\r
4796         UserAdjudicationEvent( -1 );\r
4797         break;\r
4798 \r
4799     case IDM_UserAdjudication_Draw:\r
4800         UserAdjudicationEvent( 0 );\r
4801         break;\r
4802 \r
4803     /* [AS] Game list options dialog */\r
4804     case IDM_GameListOptions:\r
4805       GameListOptions();\r
4806       break;\r
4807 \r
4808     case IDM_NewChat:\r
4809       ChatPopUp(NULL);\r
4810       break;\r
4811 \r
4812     case IDM_CopyPosition:\r
4813       CopyFENToClipboard();\r
4814       break;\r
4815 \r
4816     case IDM_PastePosition:\r
4817       PasteFENFromClipboard();\r
4818       break;\r
4819 \r
4820     case IDM_MailMove:\r
4821       MailMoveEvent();\r
4822       break;\r
4823 \r
4824     case IDM_ReloadCMailMsg:\r
4825       Reset(TRUE, TRUE);\r
4826       ReloadCmailMsgEvent(FALSE);\r
4827       break;\r
4828 \r
4829     case IDM_Minimize:\r
4830       ShowWindow(hwnd, SW_MINIMIZE);\r
4831       break;\r
4832 \r
4833     case IDM_Exit:\r
4834       ExitEvent(0);\r
4835       break;\r
4836 \r
4837     case IDM_MachineWhite:\r
4838       MachineWhiteEvent();\r
4839       /*\r
4840        * refresh the tags dialog only if it's visible\r
4841        */\r
4842       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4843           char *tags;\r
4844           tags = PGNTags(&gameInfo);\r
4845           TagsPopUp(tags, CmailMsg());\r
4846           free(tags);\r
4847       }\r
4848       SAY("computer starts playing white");\r
4849       break;\r
4850 \r
4851     case IDM_MachineBlack:\r
4852       MachineBlackEvent();\r
4853       /*\r
4854        * refresh the tags dialog only if it's visible\r
4855        */\r
4856       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4857           char *tags;\r
4858           tags = PGNTags(&gameInfo);\r
4859           TagsPopUp(tags, CmailMsg());\r
4860           free(tags);\r
4861       }\r
4862       SAY("computer starts playing black");\r
4863       break;\r
4864 \r
4865     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4866       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4867       break;\r
4868 \r
4869     case IDM_TwoMachines:\r
4870       TwoMachinesEvent();\r
4871       /*\r
4872        * refresh the tags dialog only if it's visible\r
4873        */\r
4874       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4875           char *tags;\r
4876           tags = PGNTags(&gameInfo);\r
4877           TagsPopUp(tags, CmailMsg());\r
4878           free(tags);\r
4879       }\r
4880       SAY("computer starts playing both sides");\r
4881       break;\r
4882 \r
4883     case IDM_AnalysisMode:\r
4884       if (!first.analysisSupport) {\r
4885         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4886         DisplayError(buf, 0);\r
4887       } else {\r
4888         SAY("analyzing current position");\r
4889         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4890         if (appData.icsActive) {\r
4891                if (gameMode != IcsObserving) {\r
4892                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4893                        DisplayError(buf, 0);\r
4894                        /* secure check */\r
4895                        if (appData.icsEngineAnalyze) {\r
4896                                if (appData.debugMode) \r
4897                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4898                                ExitAnalyzeMode();\r
4899                                ModeHighlight();\r
4900                                break;\r
4901                        }\r
4902                        break;\r
4903                } else {\r
4904                        /* if enable, user want disable icsEngineAnalyze */\r
4905                        if (appData.icsEngineAnalyze) {\r
4906                                ExitAnalyzeMode();\r
4907                                ModeHighlight();\r
4908                                break;\r
4909                        }\r
4910                        appData.icsEngineAnalyze = TRUE;\r
4911                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4912                }\r
4913         } \r
4914         if (!appData.showThinking) ToggleShowThinking();\r
4915         AnalyzeModeEvent();\r
4916       }\r
4917       break;\r
4918 \r
4919     case IDM_AnalyzeFile:\r
4920       if (!first.analysisSupport) {\r
4921         char buf[MSG_SIZ];\r
4922           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4923         DisplayError(buf, 0);\r
4924       } else {\r
4925         if (!appData.showThinking) ToggleShowThinking();\r
4926         AnalyzeFileEvent();\r
4927         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4928         AnalysisPeriodicEvent(1);\r
4929       }\r
4930       break;\r
4931 \r
4932     case IDM_IcsClient:\r
4933       IcsClientEvent();\r
4934       break;\r
4935 \r
4936     case IDM_EditGame:\r
4937     case IDM_EditGame2:\r
4938       EditGameEvent();\r
4939       SAY("edit game");\r
4940       break;\r
4941 \r
4942     case IDM_EditPosition:\r
4943     case IDM_EditPosition2:\r
4944       EditPositionEvent();\r
4945       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4946       break;\r
4947 \r
4948     case IDM_Training:\r
4949       TrainingEvent();\r
4950       break;\r
4951 \r
4952     case IDM_ShowGameList:\r
4953       ShowGameListProc();\r
4954       break;\r
4955 \r
4956     case IDM_EditProgs1:\r
4957       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4958       break;\r
4959 \r
4960     case IDM_EditProgs2:\r
4961      LoadEnginePopUp(hwndMain);\r
4962 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4963       break;\r
4964 \r
4965     case IDM_EditServers:\r
4966       EditTagsPopUp(icsNames, &icsNames);\r
4967       break;\r
4968 \r
4969     case IDM_EditTags:\r
4970     case IDM_Tags:\r
4971       EditTagsProc();\r
4972       break;\r
4973 \r
4974     case IDM_EditBook:\r
4975       EditBookEvent();\r
4976       break;\r
4977 \r
4978     case IDM_EditComment:\r
4979     case IDM_Comment:\r
4980       if (commentUp && editComment) {\r
4981         CommentPopDown();\r
4982       } else {\r
4983         EditCommentEvent();\r
4984       }\r
4985       break;\r
4986 \r
4987     case IDM_Pause:\r
4988       PauseEvent();\r
4989       break;\r
4990 \r
4991     case IDM_Accept:\r
4992       AcceptEvent();\r
4993       break;\r
4994 \r
4995     case IDM_Decline:\r
4996       DeclineEvent();\r
4997       break;\r
4998 \r
4999     case IDM_Rematch:\r
5000       RematchEvent();\r
5001       break;\r
5002 \r
5003     case IDM_CallFlag:\r
5004       CallFlagEvent();\r
5005       break;\r
5006 \r
5007     case IDM_Draw:\r
5008       DrawEvent();\r
5009       break;\r
5010 \r
5011     case IDM_Adjourn:\r
5012       AdjournEvent();\r
5013       break;\r
5014 \r
5015     case IDM_Abort:\r
5016       AbortEvent();\r
5017       break;\r
5018 \r
5019     case IDM_Resign:\r
5020       ResignEvent();\r
5021       break;\r
5022 \r
5023     case IDM_StopObserving:\r
5024       StopObservingEvent();\r
5025       break;\r
5026 \r
5027     case IDM_StopExamining:\r
5028       StopExaminingEvent();\r
5029       break;\r
5030 \r
5031     case IDM_Upload:\r
5032       UploadGameEvent();\r
5033       break;\r
5034 \r
5035     case IDM_TypeInMove:\r
5036       TypeInEvent('\000');\r
5037       break;\r
5038 \r
5039     case IDM_TypeInName:\r
5040       PopUpNameDialog('\000');\r
5041       break;\r
5042 \r
5043     case IDM_Backward:\r
5044       BackwardEvent();\r
5045       SetFocus(hwndMain);\r
5046       break;\r
5047 \r
5048     JAWS_MENU_ITEMS\r
5049 \r
5050     case IDM_Forward:\r
5051       ForwardEvent();\r
5052       SetFocus(hwndMain);\r
5053       break;\r
5054 \r
5055     case IDM_ToStart:\r
5056       ToStartEvent();\r
5057       SetFocus(hwndMain);\r
5058       break;\r
5059 \r
5060     case IDM_ToEnd:\r
5061       ToEndEvent();\r
5062       SetFocus(hwndMain);\r
5063       break;\r
5064 \r
5065     case IDM_Revert:\r
5066       RevertEvent(FALSE);\r
5067       break;\r
5068 \r
5069     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5070       RevertEvent(TRUE);\r
5071       break;\r
5072 \r
5073     case IDM_TruncateGame:\r
5074       TruncateGameEvent();\r
5075       break;\r
5076 \r
5077     case IDM_MoveNow:\r
5078       MoveNowEvent();\r
5079       break;\r
5080 \r
5081     case IDM_RetractMove:\r
5082       RetractMoveEvent();\r
5083       break;\r
5084 \r
5085     case IDM_FlipView:\r
5086       flipView = !flipView;\r
5087       DrawPosition(FALSE, NULL);\r
5088       break;\r
5089 \r
5090     case IDM_FlipClock:\r
5091       flipClock = !flipClock;\r
5092       DisplayBothClocks();\r
5093       DisplayLogos();\r
5094       break;\r
5095 \r
5096     case IDM_MuteSounds:\r
5097       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5098       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5099                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5100       break;\r
5101 \r
5102     case IDM_GeneralOptions:\r
5103       GeneralOptionsPopup(hwnd);\r
5104       DrawPosition(TRUE, NULL);\r
5105       break;\r
5106 \r
5107     case IDM_BoardOptions:\r
5108       BoardOptionsPopup(hwnd);\r
5109       break;\r
5110 \r
5111     case IDM_EnginePlayOptions:\r
5112       EnginePlayOptionsPopup(hwnd);\r
5113       break;\r
5114 \r
5115     case IDM_Engine1Options:\r
5116       EngineOptionsPopup(hwnd, &first);\r
5117       break;\r
5118 \r
5119     case IDM_Engine2Options:\r
5120       savedHwnd = hwnd;\r
5121       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5122       EngineOptionsPopup(hwnd, &second);\r
5123       break;\r
5124 \r
5125     case IDM_OptionsUCI:\r
5126       UciOptionsPopup(hwnd);\r
5127       break;\r
5128 \r
5129     case IDM_Tourney:\r
5130       TourneyPopup(hwnd);\r
5131       break;\r
5132 \r
5133     case IDM_IcsOptions:\r
5134       IcsOptionsPopup(hwnd);\r
5135       break;\r
5136 \r
5137     case IDM_Fonts:\r
5138       FontsOptionsPopup(hwnd);\r
5139       break;\r
5140 \r
5141     case IDM_Sounds:\r
5142       SoundOptionsPopup(hwnd);\r
5143       break;\r
5144 \r
5145     case IDM_CommPort:\r
5146       CommPortOptionsPopup(hwnd);\r
5147       break;\r
5148 \r
5149     case IDM_LoadOptions:\r
5150       LoadOptionsPopup(hwnd);\r
5151       break;\r
5152 \r
5153     case IDM_SaveOptions:\r
5154       SaveOptionsPopup(hwnd);\r
5155       break;\r
5156 \r
5157     case IDM_TimeControl:\r
5158       TimeControlOptionsPopup(hwnd);\r
5159       break;\r
5160 \r
5161     case IDM_SaveSettings:\r
5162       SaveSettings(settingsFileName);\r
5163       break;\r
5164 \r
5165     case IDM_SaveSettingsOnExit:\r
5166       saveSettingsOnExit = !saveSettingsOnExit;\r
5167       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5168                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5169                                          MF_CHECKED : MF_UNCHECKED));\r
5170       break;\r
5171 \r
5172     case IDM_Hint:\r
5173       HintEvent();\r
5174       break;\r
5175 \r
5176     case IDM_Book:\r
5177       BookEvent();\r
5178       break;\r
5179 \r
5180     case IDM_AboutGame:\r
5181       AboutGameEvent();\r
5182       break;\r
5183 \r
5184     case IDM_Debug:\r
5185       appData.debugMode = !appData.debugMode;\r
5186       if (appData.debugMode) {\r
5187         char dir[MSG_SIZ];\r
5188         GetCurrentDirectory(MSG_SIZ, dir);\r
5189         SetCurrentDirectory(installDir);\r
5190         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5191         SetCurrentDirectory(dir);\r
5192         setbuf(debugFP, NULL);\r
5193       } else {\r
5194         fclose(debugFP);\r
5195         debugFP = NULL;\r
5196       }\r
5197       break;\r
5198 \r
5199     case IDM_HELPCONTENTS:\r
5200       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5201           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5202           MessageBox (GetFocus(),\r
5203                     _("Unable to activate help"),\r
5204                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5205       }\r
5206       break;\r
5207 \r
5208     case IDM_HELPSEARCH:\r
5209         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5210             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5211         MessageBox (GetFocus(),\r
5212                     _("Unable to activate help"),\r
5213                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5214       }\r
5215       break;\r
5216 \r
5217     case IDM_HELPHELP:\r
5218       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5219         MessageBox (GetFocus(),\r
5220                     _("Unable to activate help"),\r
5221                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5222       }\r
5223       break;\r
5224 \r
5225     case IDM_ABOUT:\r
5226       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5227       DialogBox(hInst, \r
5228         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5229         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5230       FreeProcInstance(lpProc);\r
5231       break;\r
5232 \r
5233     case IDM_DirectCommand1:\r
5234       AskQuestionEvent(_("Direct Command"),\r
5235                        _("Send to chess program:"), "", "1");\r
5236       break;\r
5237     case IDM_DirectCommand2:\r
5238       AskQuestionEvent(_("Direct Command"),\r
5239                        _("Send to second chess program:"), "", "2");\r
5240       break;\r
5241 \r
5242     case EP_WhitePawn:\r
5243       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_WhiteKnight:\r
5248       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_WhiteBishop:\r
5253       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_WhiteRook:\r
5258       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_WhiteQueen:\r
5263       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteFerz:\r
5268       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_WhiteWazir:\r
5273       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_WhiteAlfil:\r
5278       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_WhiteCannon:\r
5283       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_WhiteCardinal:\r
5288       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_WhiteMarshall:\r
5293       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_WhiteKing:\r
5298       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackPawn:\r
5303       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_BlackKnight:\r
5308       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_BlackBishop:\r
5313       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_BlackRook:\r
5318       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackQueen:\r
5323       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackFerz:\r
5328       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_BlackWazir:\r
5333       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_BlackAlfil:\r
5338       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_BlackCannon:\r
5343       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_BlackCardinal:\r
5348       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_BlackMarshall:\r
5353       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_BlackKing:\r
5358       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_EmptySquare:\r
5363       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_ClearBoard:\r
5368       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_White:\r
5373       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case EP_Black:\r
5378       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case EP_Promote:\r
5383       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case EP_Demote:\r
5388       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case DP_Pawn:\r
5393       DropMenuEvent(WhitePawn, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case DP_Knight:\r
5398       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case DP_Bishop:\r
5403       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case DP_Rook:\r
5408       DropMenuEvent(WhiteRook, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case DP_Queen:\r
5413       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case IDM_English:\r
5418       barbaric = 0; appData.language = "";\r
5419       TranslateMenus(0);\r
5420       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5421       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5422       lastChecked = wmId;\r
5423       break;\r
5424 \r
5425     default:\r
5426       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5427           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5428           TranslateMenus(0);\r
5429           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5430           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5431           lastChecked = wmId;\r
5432           break;\r
5433       }\r
5434       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5435     }\r
5436     break;\r
5437 \r
5438   case WM_TIMER:\r
5439     switch (wParam) {\r
5440     case CLOCK_TIMER_ID:\r
5441       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5442       clockTimerEvent = 0;\r
5443       DecrementClocks(); /* call into back end */\r
5444       break;\r
5445     case LOAD_GAME_TIMER_ID:\r
5446       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5447       loadGameTimerEvent = 0;\r
5448       AutoPlayGameLoop(); /* call into back end */\r
5449       break;\r
5450     case ANALYSIS_TIMER_ID:\r
5451       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5452                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5453         AnalysisPeriodicEvent(0);\r
5454       } else {\r
5455         KillTimer(hwnd, analysisTimerEvent);\r
5456         analysisTimerEvent = 0;\r
5457       }\r
5458       break;\r
5459     case DELAYED_TIMER_ID:\r
5460       KillTimer(hwnd, delayedTimerEvent);\r
5461       delayedTimerEvent = 0;\r
5462       delayedTimerCallback();\r
5463       break;\r
5464     }\r
5465     break;\r
5466 \r
5467   case WM_USER_Input:\r
5468     InputEvent(hwnd, message, wParam, lParam);\r
5469     break;\r
5470 \r
5471   /* [AS] Also move "attached" child windows */\r
5472   case WM_WINDOWPOSCHANGING:\r
5473 \r
5474     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5475         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5476 \r
5477         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5478             /* Window is moving */\r
5479             RECT rcMain;\r
5480 \r
5481 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5482             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5483             rcMain.right  = wpMain.x + wpMain.width;\r
5484             rcMain.top    = wpMain.y;\r
5485             rcMain.bottom = wpMain.y + wpMain.height;\r
5486             \r
5487             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5488             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5489             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5490             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5491             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5492             wpMain.x = lpwp->x;\r
5493             wpMain.y = lpwp->y;\r
5494         }\r
5495     }\r
5496     break;\r
5497 \r
5498   /* [AS] Snapping */\r
5499   case WM_ENTERSIZEMOVE:\r
5500     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5501     if (hwnd == hwndMain) {\r
5502       doingSizing = TRUE;\r
5503       lastSizing = 0;\r
5504     }\r
5505     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5506     break;\r
5507 \r
5508   case WM_SIZING:\r
5509     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5510     if (hwnd == hwndMain) {\r
5511       lastSizing = wParam;\r
5512     }\r
5513     break;\r
5514 \r
5515   case WM_MOVING:\r
5516     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5517       return OnMoving( &sd, hwnd, wParam, lParam );\r
5518 \r
5519   case WM_EXITSIZEMOVE:\r
5520     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5521     if (hwnd == hwndMain) {\r
5522       RECT client;\r
5523       doingSizing = FALSE;\r
5524       InvalidateRect(hwnd, &boardRect, FALSE);\r
5525       GetClientRect(hwnd, &client);\r
5526       ResizeBoard(client.right, client.bottom, lastSizing);\r
5527       lastSizing = 0;\r
5528       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5529     }\r
5530     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5531     break;\r
5532 \r
5533   case WM_DESTROY: /* message: window being destroyed */\r
5534     PostQuitMessage(0);\r
5535     break;\r
5536 \r
5537   case WM_CLOSE:\r
5538     if (hwnd == hwndMain) {\r
5539       ExitEvent(0);\r
5540     }\r
5541     break;\r
5542 \r
5543   default:      /* Passes it on if unprocessed */\r
5544     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5545   }\r
5546   return 0;\r
5547 }\r
5548 \r
5549 /*---------------------------------------------------------------------------*\\r
5550  *\r
5551  * Misc utility routines\r
5552  *\r
5553 \*---------------------------------------------------------------------------*/\r
5554 \r
5555 /*\r
5556  * Decent random number generator, at least not as bad as Windows\r
5557  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5558  */\r
5559 unsigned int randstate;\r
5560 \r
5561 int\r
5562 myrandom(void)\r
5563 {\r
5564   randstate = randstate * 1664525 + 1013904223;\r
5565   return (int) randstate & 0x7fffffff;\r
5566 }\r
5567 \r
5568 void\r
5569 mysrandom(unsigned int seed)\r
5570 {\r
5571   randstate = seed;\r
5572 }\r
5573 \r
5574 \r
5575 /* \r
5576  * returns TRUE if user selects a different color, FALSE otherwise \r
5577  */\r
5578 \r
5579 BOOL\r
5580 ChangeColor(HWND hwnd, COLORREF *which)\r
5581 {\r
5582   static BOOL firstTime = TRUE;\r
5583   static DWORD customColors[16];\r
5584   CHOOSECOLOR cc;\r
5585   COLORREF newcolor;\r
5586   int i;\r
5587   ColorClass ccl;\r
5588 \r
5589   if (firstTime) {\r
5590     /* Make initial colors in use available as custom colors */\r
5591     /* Should we put the compiled-in defaults here instead? */\r
5592     i = 0;\r
5593     customColors[i++] = lightSquareColor & 0xffffff;\r
5594     customColors[i++] = darkSquareColor & 0xffffff;\r
5595     customColors[i++] = whitePieceColor & 0xffffff;\r
5596     customColors[i++] = blackPieceColor & 0xffffff;\r
5597     customColors[i++] = highlightSquareColor & 0xffffff;\r
5598     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5599 \r
5600     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5601       customColors[i++] = textAttribs[ccl].color;\r
5602     }\r
5603     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5604     firstTime = FALSE;\r
5605   }\r
5606 \r
5607   cc.lStructSize = sizeof(cc);\r
5608   cc.hwndOwner = hwnd;\r
5609   cc.hInstance = NULL;\r
5610   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5611   cc.lpCustColors = (LPDWORD) customColors;\r
5612   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5613 \r
5614   if (!ChooseColor(&cc)) return FALSE;\r
5615 \r
5616   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5617   if (newcolor == *which) return FALSE;\r
5618   *which = newcolor;\r
5619   return TRUE;\r
5620 \r
5621   /*\r
5622   InitDrawingColors();\r
5623   InvalidateRect(hwnd, &boardRect, FALSE);\r
5624   */\r
5625 }\r
5626 \r
5627 BOOLEAN\r
5628 MyLoadSound(MySound *ms)\r
5629 {\r
5630   BOOL ok = FALSE;\r
5631   struct stat st;\r
5632   FILE *f;\r
5633 \r
5634   if (ms->data && ms->flag) free(ms->data);\r
5635   ms->data = NULL;\r
5636 \r
5637   switch (ms->name[0]) {\r
5638   case NULLCHAR:\r
5639     /* Silence */\r
5640     ok = TRUE;\r
5641     break;\r
5642   case '$':\r
5643     /* System sound from Control Panel.  Don't preload here. */\r
5644     ok = TRUE;\r
5645     break;\r
5646   case '!':\r
5647     if (ms->name[1] == NULLCHAR) {\r
5648       /* "!" alone = silence */\r
5649       ok = TRUE;\r
5650     } else {\r
5651       /* Builtin wave resource.  Error if not found. */\r
5652       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5653       if (h == NULL) break;\r
5654       ms->data = (void *)LoadResource(hInst, h);\r
5655       ms->flag = 0; // not maloced, so cannot be freed!\r
5656       if (h == NULL) break;\r
5657       ok = TRUE;\r
5658     }\r
5659     break;\r
5660   default:\r
5661     /* .wav file.  Error if not found. */\r
5662     f = fopen(ms->name, "rb");\r
5663     if (f == NULL) break;\r
5664     if (fstat(fileno(f), &st) < 0) break;\r
5665     ms->data = malloc(st.st_size);\r
5666     ms->flag = 1;\r
5667     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5668     fclose(f);\r
5669     ok = TRUE;\r
5670     break;\r
5671   }\r
5672   if (!ok) {\r
5673     char buf[MSG_SIZ];\r
5674       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5675     DisplayError(buf, GetLastError());\r
5676   }\r
5677   return ok;\r
5678 }\r
5679 \r
5680 BOOLEAN\r
5681 MyPlaySound(MySound *ms)\r
5682 {\r
5683   BOOLEAN ok = FALSE;\r
5684 \r
5685   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5686   switch (ms->name[0]) {\r
5687   case NULLCHAR:\r
5688         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5689     /* Silence */\r
5690     ok = TRUE;\r
5691     break;\r
5692   case '$':\r
5693     /* System sound from Control Panel (deprecated feature).\r
5694        "$" alone or an unset sound name gets default beep (still in use). */\r
5695     if (ms->name[1]) {\r
5696       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5697     }\r
5698     if (!ok) ok = MessageBeep(MB_OK);\r
5699     break; \r
5700   case '!':\r
5701     /* Builtin wave resource, or "!" alone for silence */\r
5702     if (ms->name[1]) {\r
5703       if (ms->data == NULL) return FALSE;\r
5704       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5705     } else {\r
5706       ok = TRUE;\r
5707     }\r
5708     break;\r
5709   default:\r
5710     /* .wav file.  Error if not found. */\r
5711     if (ms->data == NULL) return FALSE;\r
5712     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5713     break;\r
5714   }\r
5715   /* Don't print an error: this can happen innocently if the sound driver\r
5716      is busy; for instance, if another instance of WinBoard is playing\r
5717      a sound at about the same time. */\r
5718   return ok;\r
5719 }\r
5720 \r
5721 \r
5722 LRESULT CALLBACK\r
5723 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5724 {\r
5725   BOOL ok;\r
5726   OPENFILENAME *ofn;\r
5727   static UINT *number; /* gross that this is static */\r
5728 \r
5729   switch (message) {\r
5730   case WM_INITDIALOG: /* message: initialize dialog box */\r
5731     /* Center the dialog over the application window */\r
5732     ofn = (OPENFILENAME *) lParam;\r
5733     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5734       number = (UINT *) ofn->lCustData;\r
5735       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5736     } else {\r
5737       number = NULL;\r
5738     }\r
5739     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5740     Translate(hDlg, 1536);\r
5741     return FALSE;  /* Allow for further processing */\r
5742 \r
5743   case WM_COMMAND:\r
5744     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5745       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5746     }\r
5747     return FALSE;  /* Allow for further processing */\r
5748   }\r
5749   return FALSE;\r
5750 }\r
5751 \r
5752 UINT APIENTRY\r
5753 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5754 {\r
5755   static UINT *number;\r
5756   OPENFILENAME *ofname;\r
5757   OFNOTIFY *ofnot;\r
5758   switch (uiMsg) {\r
5759   case WM_INITDIALOG:\r
5760     Translate(hdlg, DLG_IndexNumber);\r
5761     ofname = (OPENFILENAME *)lParam;\r
5762     number = (UINT *)(ofname->lCustData);\r
5763     break;\r
5764   case WM_NOTIFY:\r
5765     ofnot = (OFNOTIFY *)lParam;\r
5766     if (ofnot->hdr.code == CDN_FILEOK) {\r
5767       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5768     }\r
5769     break;\r
5770   }\r
5771   return 0;\r
5772 }\r
5773 \r
5774 \r
5775 FILE *\r
5776 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5777                char *nameFilt, char *dlgTitle, UINT *number,\r
5778                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5779 {\r
5780   OPENFILENAME openFileName;\r
5781   char buf1[MSG_SIZ];\r
5782   FILE *f;\r
5783 \r
5784   if (fileName == NULL) fileName = buf1;\r
5785   if (defName == NULL) {\r
5786     safeStrCpy(fileName, "*.", 3 );\r
5787     strcat(fileName, defExt);\r
5788   } else {\r
5789     safeStrCpy(fileName, defName, MSG_SIZ );\r
5790   }\r
5791     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5792   if (number) *number = 0;\r
5793 \r
5794   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5795   openFileName.hwndOwner         = hwnd;\r
5796   openFileName.hInstance         = (HANDLE) hInst;\r
5797   openFileName.lpstrFilter       = nameFilt;\r
5798   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5799   openFileName.nMaxCustFilter    = 0L;\r
5800   openFileName.nFilterIndex      = 1L;\r
5801   openFileName.lpstrFile         = fileName;\r
5802   openFileName.nMaxFile          = MSG_SIZ;\r
5803   openFileName.lpstrFileTitle    = fileTitle;\r
5804   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5805   openFileName.lpstrInitialDir   = NULL;\r
5806   openFileName.lpstrTitle        = dlgTitle;\r
5807   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5808     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5809     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5810     | (oldDialog ? 0 : OFN_EXPLORER);\r
5811   openFileName.nFileOffset       = 0;\r
5812   openFileName.nFileExtension    = 0;\r
5813   openFileName.lpstrDefExt       = defExt;\r
5814   openFileName.lCustData         = (LONG) number;\r
5815   openFileName.lpfnHook          = oldDialog ?\r
5816     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5817   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5818 \r
5819   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5820                         GetOpenFileName(&openFileName)) {\r
5821     /* open the file */\r
5822     f = fopen(openFileName.lpstrFile, write);\r
5823     if (f == NULL) {\r
5824       MessageBox(hwnd, _("File open failed"), NULL,\r
5825                  MB_OK|MB_ICONEXCLAMATION);\r
5826       return NULL;\r
5827     }\r
5828   } else {\r
5829     int err = CommDlgExtendedError();\r
5830     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5831     return FALSE;\r
5832   }\r
5833   return f;\r
5834 }\r
5835 \r
5836 \r
5837 \r
5838 VOID APIENTRY\r
5839 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5840 {\r
5841   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5842 \r
5843   /*\r
5844    * Get the first pop-up menu in the menu template. This is the\r
5845    * menu that TrackPopupMenu displays.\r
5846    */\r
5847   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5848   TranslateOneMenu(10, hmenuTrackPopup);\r
5849 \r
5850   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5851 \r
5852   /*\r
5853    * TrackPopup uses screen coordinates, so convert the\r
5854    * coordinates of the mouse click to screen coordinates.\r
5855    */\r
5856   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5857 \r
5858   /* Draw and track the floating pop-up menu. */\r
5859   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5860                  pt.x, pt.y, 0, hwnd, NULL);\r
5861 \r
5862   /* Destroy the menu.*/\r
5863   DestroyMenu(hmenu);\r
5864 }\r
5865    \r
5866 typedef struct {\r
5867   HWND hDlg, hText;\r
5868   int sizeX, sizeY, newSizeX, newSizeY;\r
5869   HDWP hdwp;\r
5870 } ResizeEditPlusButtonsClosure;\r
5871 \r
5872 BOOL CALLBACK\r
5873 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5874 {\r
5875   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5876   RECT rect;\r
5877   POINT pt;\r
5878 \r
5879   if (hChild == cl->hText) return TRUE;\r
5880   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5881   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5882   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5883   ScreenToClient(cl->hDlg, &pt);\r
5884   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5885     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5886   return TRUE;\r
5887 }\r
5888 \r
5889 /* Resize a dialog that has a (rich) edit field filling most of\r
5890    the top, with a row of buttons below */\r
5891 VOID\r
5892 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5893 {\r
5894   RECT rectText;\r
5895   int newTextHeight, newTextWidth;\r
5896   ResizeEditPlusButtonsClosure cl;\r
5897   \r
5898   /*if (IsIconic(hDlg)) return;*/\r
5899   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5900   \r
5901   cl.hdwp = BeginDeferWindowPos(8);\r
5902 \r
5903   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5904   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5905   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5906   if (newTextHeight < 0) {\r
5907     newSizeY += -newTextHeight;\r
5908     newTextHeight = 0;\r
5909   }\r
5910   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5911     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5912 \r
5913   cl.hDlg = hDlg;\r
5914   cl.hText = hText;\r
5915   cl.sizeX = sizeX;\r
5916   cl.sizeY = sizeY;\r
5917   cl.newSizeX = newSizeX;\r
5918   cl.newSizeY = newSizeY;\r
5919   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5920 \r
5921   EndDeferWindowPos(cl.hdwp);\r
5922 }\r
5923 \r
5924 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5925 {\r
5926     RECT    rChild, rParent;\r
5927     int     wChild, hChild, wParent, hParent;\r
5928     int     wScreen, hScreen, xNew, yNew;\r
5929     HDC     hdc;\r
5930 \r
5931     /* Get the Height and Width of the child window */\r
5932     GetWindowRect (hwndChild, &rChild);\r
5933     wChild = rChild.right - rChild.left;\r
5934     hChild = rChild.bottom - rChild.top;\r
5935 \r
5936     /* Get the Height and Width of the parent window */\r
5937     GetWindowRect (hwndParent, &rParent);\r
5938     wParent = rParent.right - rParent.left;\r
5939     hParent = rParent.bottom - rParent.top;\r
5940 \r
5941     /* Get the display limits */\r
5942     hdc = GetDC (hwndChild);\r
5943     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5944     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5945     ReleaseDC(hwndChild, hdc);\r
5946 \r
5947     /* Calculate new X position, then adjust for screen */\r
5948     xNew = rParent.left + ((wParent - wChild) /2);\r
5949     if (xNew < 0) {\r
5950         xNew = 0;\r
5951     } else if ((xNew+wChild) > wScreen) {\r
5952         xNew = wScreen - wChild;\r
5953     }\r
5954 \r
5955     /* Calculate new Y position, then adjust for screen */\r
5956     if( mode == 0 ) {\r
5957         yNew = rParent.top  + ((hParent - hChild) /2);\r
5958     }\r
5959     else {\r
5960         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5961     }\r
5962 \r
5963     if (yNew < 0) {\r
5964         yNew = 0;\r
5965     } else if ((yNew+hChild) > hScreen) {\r
5966         yNew = hScreen - hChild;\r
5967     }\r
5968 \r
5969     /* Set it, and return */\r
5970     return SetWindowPos (hwndChild, NULL,\r
5971                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5972 }\r
5973 \r
5974 /* Center one window over another */\r
5975 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5976 {\r
5977     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5978 }\r
5979 \r
5980 /*---------------------------------------------------------------------------*\\r
5981  *\r
5982  * Startup Dialog functions\r
5983  *\r
5984 \*---------------------------------------------------------------------------*/\r
5985 void\r
5986 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5987 {\r
5988   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5989 \r
5990   while (*cd != NULL) {\r
5991     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5992     cd++;\r
5993   }\r
5994 }\r
5995 \r
5996 void\r
5997 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5998 {\r
5999   char buf1[MAX_ARG_LEN];\r
6000   int len;\r
6001 \r
6002   if (str[0] == '@') {\r
6003     FILE* f = fopen(str + 1, "r");\r
6004     if (f == NULL) {\r
6005       DisplayFatalError(str + 1, errno, 2);\r
6006       return;\r
6007     }\r
6008     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6009     fclose(f);\r
6010     buf1[len] = NULLCHAR;\r
6011     str = buf1;\r
6012   }\r
6013 \r
6014   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6015 \r
6016   for (;;) {\r
6017     char buf[MSG_SIZ];\r
6018     char *end = strchr(str, '\n');\r
6019     if (end == NULL) return;\r
6020     memcpy(buf, str, end - str);\r
6021     buf[end - str] = NULLCHAR;\r
6022     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6023     str = end + 1;\r
6024   }\r
6025 }\r
6026 \r
6027 void\r
6028 SetStartupDialogEnables(HWND hDlg)\r
6029 {\r
6030   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6031     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6032     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6033   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6034     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6035   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6036     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6037   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6038     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6039   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6040     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6041     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6042     IsDlgButtonChecked(hDlg, OPT_View));\r
6043 }\r
6044 \r
6045 char *\r
6046 QuoteForFilename(char *filename)\r
6047 {\r
6048   int dquote, space;\r
6049   dquote = strchr(filename, '"') != NULL;\r
6050   space = strchr(filename, ' ') != NULL;\r
6051   if (dquote || space) {\r
6052     if (dquote) {\r
6053       return "'";\r
6054     } else {\r
6055       return "\"";\r
6056     }\r
6057   } else {\r
6058     return "";\r
6059   }\r
6060 }\r
6061 \r
6062 VOID\r
6063 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6064 {\r
6065   char buf[MSG_SIZ];\r
6066   char *q;\r
6067 \r
6068   InitComboStringsFromOption(hwndCombo, nthnames);\r
6069   q = QuoteForFilename(nthcp);\r
6070     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6071   if (*nthdir != NULLCHAR) {\r
6072     q = QuoteForFilename(nthdir);\r
6073       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6074   }\r
6075   if (*nthcp == NULLCHAR) {\r
6076     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6077   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6078     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6079     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6080   }\r
6081 }\r
6082 \r
6083 LRESULT CALLBACK\r
6084 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6085 {\r
6086   char buf[MSG_SIZ];\r
6087   HANDLE hwndCombo;\r
6088   char *p;\r
6089 \r
6090   switch (message) {\r
6091   case WM_INITDIALOG:\r
6092     /* Center the dialog */\r
6093     CenterWindow (hDlg, GetDesktopWindow());\r
6094     Translate(hDlg, DLG_Startup);\r
6095     /* Initialize the dialog items */\r
6096     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6097                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6098                   firstChessProgramNames);\r
6099     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6100                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6101                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6102     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6103     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6104       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6105     if (*appData.icsHelper != NULLCHAR) {\r
6106       char *q = QuoteForFilename(appData.icsHelper);\r
6107       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6108     }\r
6109     if (*appData.icsHost == NULLCHAR) {\r
6110       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6111       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6112     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6113       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6114       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6115     }\r
6116 \r
6117     if (appData.icsActive) {\r
6118       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6119     }\r
6120     else if (appData.noChessProgram) {\r
6121       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6122     }\r
6123     else {\r
6124       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6125     }\r
6126 \r
6127     SetStartupDialogEnables(hDlg);\r
6128     return TRUE;\r
6129 \r
6130   case WM_COMMAND:\r
6131     switch (LOWORD(wParam)) {\r
6132     case IDOK:\r
6133       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6134         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6135         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6136         p = buf;\r
6137         ParseArgs(StringGet, &p);\r
6138         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6139         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6140         p = buf;
6141         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6142         ParseArgs(StringGet, &p);\r
6143         SwapEngines(singleList); // ... and then make it 'second'\r
6144         appData.noChessProgram = FALSE;\r
6145         appData.icsActive = FALSE;\r
6146       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6147         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6148         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6149         p = buf;\r
6150         ParseArgs(StringGet, &p);\r
6151         if (appData.zippyPlay) {\r
6152           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6153           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6154           p = buf;\r
6155           ParseArgs(StringGet, &p);\r
6156         }\r
6157       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6158         appData.noChessProgram = TRUE;\r
6159         appData.icsActive = FALSE;\r
6160       } else {\r
6161         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6162                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6163         return TRUE;\r
6164       }\r
6165       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6166         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6167         p = buf;\r
6168         ParseArgs(StringGet, &p);\r
6169       }\r
6170       EndDialog(hDlg, TRUE);\r
6171       return TRUE;\r
6172 \r
6173     case IDCANCEL:\r
6174       ExitEvent(0);\r
6175       return TRUE;\r
6176 \r
6177     case IDM_HELPCONTENTS:\r
6178       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6179         MessageBox (GetFocus(),\r
6180                     _("Unable to activate help"),\r
6181                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6182       }\r
6183       break;\r
6184 \r
6185     default:\r
6186       SetStartupDialogEnables(hDlg);\r
6187       break;\r
6188     }\r
6189     break;\r
6190   }\r
6191   return FALSE;\r
6192 }\r
6193 \r
6194 /*---------------------------------------------------------------------------*\\r
6195  *\r
6196  * About box dialog functions\r
6197  *\r
6198 \*---------------------------------------------------------------------------*/\r
6199 \r
6200 /* Process messages for "About" dialog box */\r
6201 LRESULT CALLBACK\r
6202 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6203 {\r
6204   switch (message) {\r
6205   case WM_INITDIALOG: /* message: initialize dialog box */\r
6206     /* Center the dialog over the application window */\r
6207     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6208     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6209     Translate(hDlg, ABOUTBOX);\r
6210     JAWS_COPYRIGHT\r
6211     return (TRUE);\r
6212 \r
6213   case WM_COMMAND: /* message: received a command */\r
6214     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6215         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6216       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6217       return (TRUE);\r
6218     }\r
6219     break;\r
6220   }\r
6221   return (FALSE);\r
6222 }\r
6223 \r
6224 /*---------------------------------------------------------------------------*\\r
6225  *\r
6226  * Comment Dialog functions\r
6227  *\r
6228 \*---------------------------------------------------------------------------*/\r
6229 \r
6230 LRESULT CALLBACK\r
6231 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6232 {\r
6233   static HANDLE hwndText = NULL;\r
6234   int len, newSizeX, newSizeY, flags;\r
6235   static int sizeX, sizeY;\r
6236   char *str;\r
6237   RECT rect;\r
6238   MINMAXINFO *mmi;\r
6239 \r
6240   switch (message) {\r
6241   case WM_INITDIALOG: /* message: initialize dialog box */\r
6242     /* Initialize the dialog items */\r
6243     Translate(hDlg, DLG_EditComment);\r
6244     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6245     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6246     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6247     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6248     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6249     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6250     SetWindowText(hDlg, commentTitle);\r
6251     if (editComment) {\r
6252       SetFocus(hwndText);\r
6253     } else {\r
6254       SetFocus(GetDlgItem(hDlg, IDOK));\r
6255     }\r
6256     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6257                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6258                 MAKELPARAM(FALSE, 0));\r
6259     /* Size and position the dialog */\r
6260     if (!commentDialog) {\r
6261       commentDialog = hDlg;\r
6262       flags = SWP_NOZORDER;\r
6263       GetClientRect(hDlg, &rect);\r
6264       sizeX = rect.right;\r
6265       sizeY = rect.bottom;\r
6266       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6267           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6268         WINDOWPLACEMENT wp;\r
6269         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6270         wp.length = sizeof(WINDOWPLACEMENT);\r
6271         wp.flags = 0;\r
6272         wp.showCmd = SW_SHOW;\r
6273         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6274         wp.rcNormalPosition.left = wpComment.x;\r
6275         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6276         wp.rcNormalPosition.top = wpComment.y;\r
6277         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6278         SetWindowPlacement(hDlg, &wp);\r
6279 \r
6280         GetClientRect(hDlg, &rect);\r
6281         newSizeX = rect.right;\r
6282         newSizeY = rect.bottom;\r
6283         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6284                               newSizeX, newSizeY);\r
6285         sizeX = newSizeX;\r
6286         sizeY = newSizeY;\r
6287       }\r
6288     }\r
6289     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6290     return FALSE;\r
6291 \r
6292   case WM_COMMAND: /* message: received a command */\r
6293     switch (LOWORD(wParam)) {\r
6294     case IDOK:\r
6295       if (editComment) {\r
6296         char *p, *q;\r
6297         /* Read changed options from the dialog box */\r
6298         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6299         len = GetWindowTextLength(hwndText);\r
6300         str = (char *) malloc(len + 1);\r
6301         GetWindowText(hwndText, str, len + 1);\r
6302         p = q = str;\r
6303         while (*q) {\r
6304           if (*q == '\r')\r
6305             q++;\r
6306           else\r
6307             *p++ = *q++;\r
6308         }\r
6309         *p = NULLCHAR;\r
6310         ReplaceComment(commentIndex, str);\r
6311         free(str);\r
6312       }\r
6313       CommentPopDown();\r
6314       return TRUE;\r
6315 \r
6316     case IDCANCEL:\r
6317     case OPT_CancelComment:\r
6318       CommentPopDown();\r
6319       return TRUE;\r
6320 \r
6321     case OPT_ClearComment:\r
6322       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6323       break;\r
6324 \r
6325     case OPT_EditComment:\r
6326       EditCommentEvent();\r
6327       return TRUE;\r
6328 \r
6329     default:\r
6330       break;\r
6331     }\r
6332     break;\r
6333 \r
6334   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6335         if( wParam == OPT_CommentText ) {\r
6336             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6337 \r
6338             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6339                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6340                 POINTL pt;\r
6341                 LRESULT index;\r
6342 \r
6343                 pt.x = LOWORD( lpMF->lParam );\r
6344                 pt.y = HIWORD( lpMF->lParam );\r
6345 \r
6346                 if(lpMF->msg == WM_CHAR) {\r
6347                         CHARRANGE sel;\r
6348                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6349                         index = sel.cpMin;\r
6350                 } else\r
6351                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6352 \r
6353                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6354                 len = GetWindowTextLength(hwndText);\r
6355                 str = (char *) malloc(len + 1);\r
6356                 GetWindowText(hwndText, str, len + 1);\r
6357                 ReplaceComment(commentIndex, str);\r
6358                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6359                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6360                 free(str);\r
6361 \r
6362                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6363                 lpMF->msg = WM_USER;\r
6364 \r
6365                 return TRUE;\r
6366             }\r
6367         }\r
6368         break;\r
6369 \r
6370   case WM_SIZE:\r
6371     newSizeX = LOWORD(lParam);\r
6372     newSizeY = HIWORD(lParam);\r
6373     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6374     sizeX = newSizeX;\r
6375     sizeY = newSizeY;\r
6376     break;\r
6377 \r
6378   case WM_GETMINMAXINFO:\r
6379     /* Prevent resizing window too small */\r
6380     mmi = (MINMAXINFO *) lParam;\r
6381     mmi->ptMinTrackSize.x = 100;\r
6382     mmi->ptMinTrackSize.y = 100;\r
6383     break;\r
6384   }\r
6385   return FALSE;\r
6386 }\r
6387 \r
6388 VOID\r
6389 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6390 {\r
6391   FARPROC lpProc;\r
6392   char *p, *q;\r
6393 \r
6394   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6395 \r
6396   if (str == NULL) str = "";\r
6397   p = (char *) malloc(2 * strlen(str) + 2);\r
6398   q = p;\r
6399   while (*str) {\r
6400     if (*str == '\n') *q++ = '\r';\r
6401     *q++ = *str++;\r
6402   }\r
6403   *q = NULLCHAR;\r
6404   if (commentText != NULL) free(commentText);\r
6405 \r
6406   commentIndex = index;\r
6407   commentTitle = title;\r
6408   commentText = p;\r
6409   editComment = edit;\r
6410 \r
6411   if (commentDialog) {\r
6412     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6413     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6414   } else {\r
6415     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6416     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6417                  hwndMain, (DLGPROC)lpProc);\r
6418     FreeProcInstance(lpProc);\r
6419   }\r
6420   commentUp = TRUE;\r
6421 }\r
6422 \r
6423 \r
6424 /*---------------------------------------------------------------------------*\\r
6425  *\r
6426  * Type-in move dialog functions\r
6427  * \r
6428 \*---------------------------------------------------------------------------*/\r
6429 \r
6430 LRESULT CALLBACK\r
6431 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6432 {\r
6433   char move[MSG_SIZ];\r
6434   HWND hInput;\r
6435 \r
6436   switch (message) {\r
6437   case WM_INITDIALOG:\r
6438     move[0] = (char) lParam;\r
6439     move[1] = NULLCHAR;\r
6440     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6441     Translate(hDlg, DLG_TypeInMove);\r
6442     hInput = GetDlgItem(hDlg, OPT_Move);\r
6443     SetWindowText(hInput, move);\r
6444     SetFocus(hInput);\r
6445     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6446     return FALSE;\r
6447 \r
6448   case WM_COMMAND:\r
6449     switch (LOWORD(wParam)) {\r
6450     case IDOK:
6451 \r
6452       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6453       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6454       TypeInDoneEvent(move);\r
6455       EndDialog(hDlg, TRUE);\r
6456       return TRUE;\r
6457     case IDCANCEL:\r
6458       EndDialog(hDlg, FALSE);\r
6459       return TRUE;\r
6460     default:\r
6461       break;\r
6462     }\r
6463     break;\r
6464   }\r
6465   return FALSE;\r
6466 }\r
6467 \r
6468 VOID\r
6469 PopUpMoveDialog(char firstchar)\r
6470 {\r
6471     FARPROC lpProc;\r
6472 \r
6473       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6474       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6475         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6476       FreeProcInstance(lpProc);\r
6477 }\r
6478 \r
6479 /*---------------------------------------------------------------------------*\\r
6480  *\r
6481  * Type-in name dialog functions\r
6482  * \r
6483 \*---------------------------------------------------------------------------*/\r
6484 \r
6485 LRESULT CALLBACK\r
6486 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6487 {\r
6488   char move[MSG_SIZ];\r
6489   HWND hInput;\r
6490 \r
6491   switch (message) {\r
6492   case WM_INITDIALOG:\r
6493     move[0] = (char) lParam;\r
6494     move[1] = NULLCHAR;\r
6495     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6496     Translate(hDlg, DLG_TypeInName);\r
6497     hInput = GetDlgItem(hDlg, OPT_Name);\r
6498     SetWindowText(hInput, move);\r
6499     SetFocus(hInput);\r
6500     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6501     return FALSE;\r
6502 \r
6503   case WM_COMMAND:\r
6504     switch (LOWORD(wParam)) {\r
6505     case IDOK:\r
6506       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6507       appData.userName = strdup(move);\r
6508       SetUserLogo();\r
6509       SetGameInfo();\r
6510       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6511         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6512         DisplayTitle(move);\r
6513       }\r
6514 \r
6515 \r
6516       EndDialog(hDlg, TRUE);\r
6517       return TRUE;\r
6518     case IDCANCEL:\r
6519       EndDialog(hDlg, FALSE);\r
6520       return TRUE;\r
6521     default:\r
6522       break;\r
6523     }\r
6524     break;\r
6525   }\r
6526   return FALSE;\r
6527 }\r
6528 \r
6529 VOID\r
6530 PopUpNameDialog(char firstchar)\r
6531 {\r
6532     FARPROC lpProc;\r
6533     \r
6534       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6535       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6536         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6537       FreeProcInstance(lpProc);\r
6538 }\r
6539 \r
6540 /*---------------------------------------------------------------------------*\\r
6541  *\r
6542  *  Error dialogs\r
6543  * \r
6544 \*---------------------------------------------------------------------------*/\r
6545 \r
6546 /* Nonmodal error box */\r
6547 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6548                              WPARAM wParam, LPARAM lParam);\r
6549 \r
6550 VOID\r
6551 ErrorPopUp(char *title, char *content)\r
6552 {\r
6553   FARPROC lpProc;\r
6554   char *p, *q;\r
6555   BOOLEAN modal = hwndMain == NULL;\r
6556 \r
6557   p = content;\r
6558   q = errorMessage;\r
6559   while (*p) {\r
6560     if (*p == '\n') {\r
6561       if (modal) {\r
6562         *q++ = ' ';\r
6563         p++;\r
6564       } else {\r
6565         *q++ = '\r';\r
6566         *q++ = *p++;\r
6567       }\r
6568     } else {\r
6569       *q++ = *p++;\r
6570     }\r
6571   }\r
6572   *q = NULLCHAR;\r
6573   strncpy(errorTitle, title, sizeof(errorTitle));\r
6574   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6575   \r
6576   if (modal) {\r
6577     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6578   } else {\r
6579     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6580     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6581                  hwndMain, (DLGPROC)lpProc);\r
6582     FreeProcInstance(lpProc);\r
6583   }\r
6584 }\r
6585 \r
6586 VOID\r
6587 ErrorPopDown()\r
6588 {\r
6589   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6590   if (errorDialog == NULL) return;\r
6591   DestroyWindow(errorDialog);\r
6592   errorDialog = NULL;\r
6593   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6594 }\r
6595 \r
6596 LRESULT CALLBACK\r
6597 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6598 {\r
6599   HANDLE hwndText;\r
6600   RECT rChild;\r
6601 \r
6602   switch (message) {\r
6603   case WM_INITDIALOG:\r
6604     GetWindowRect(hDlg, &rChild);\r
6605 \r
6606     /*\r
6607     SetWindowPos(hDlg, NULL, rChild.left,\r
6608       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6609       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6610     */\r
6611 \r
6612     /* \r
6613         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6614         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6615         and it doesn't work when you resize the dialog.\r
6616         For now, just give it a default position.\r
6617     */\r
6618     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6619     Translate(hDlg, DLG_Error);\r
6620 \r
6621     errorDialog = hDlg;\r
6622     SetWindowText(hDlg, errorTitle);\r
6623     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6624     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6625     return FALSE;\r
6626 \r
6627   case WM_COMMAND:\r
6628     switch (LOWORD(wParam)) {\r
6629     case IDOK:\r
6630     case IDCANCEL:\r
6631       if (errorDialog == hDlg) errorDialog = NULL;\r
6632       DestroyWindow(hDlg);\r
6633       return TRUE;\r
6634 \r
6635     default:\r
6636       break;\r
6637     }\r
6638     break;\r
6639   }\r
6640   return FALSE;\r
6641 }\r
6642 \r
6643 #ifdef GOTHIC\r
6644 HWND gothicDialog = NULL;\r
6645 \r
6646 LRESULT CALLBACK\r
6647 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6648 {\r
6649   HANDLE hwndText;\r
6650   RECT rChild;\r
6651   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6652 \r
6653   switch (message) {\r
6654   case WM_INITDIALOG:\r
6655     GetWindowRect(hDlg, &rChild);\r
6656 \r
6657     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6658                                                              SWP_NOZORDER);\r
6659 \r
6660     /* \r
6661         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6662         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6663         and it doesn't work when you resize the dialog.\r
6664         For now, just give it a default position.\r
6665     */\r
6666     gothicDialog = hDlg;\r
6667     SetWindowText(hDlg, errorTitle);\r
6668     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6669     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6670     return FALSE;\r
6671 \r
6672   case WM_COMMAND:\r
6673     switch (LOWORD(wParam)) {\r
6674     case IDOK:\r
6675     case IDCANCEL:\r
6676       if (errorDialog == hDlg) errorDialog = NULL;\r
6677       DestroyWindow(hDlg);\r
6678       return TRUE;\r
6679 \r
6680     default:\r
6681       break;\r
6682     }\r
6683     break;\r
6684   }\r
6685   return FALSE;\r
6686 }\r
6687 \r
6688 VOID\r
6689 GothicPopUp(char *title, VariantClass variant)\r
6690 {\r
6691   FARPROC lpProc;\r
6692   static char *lastTitle;\r
6693 \r
6694   strncpy(errorTitle, title, sizeof(errorTitle));\r
6695   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6696 \r
6697   if(lastTitle != title && gothicDialog != NULL) {\r
6698     DestroyWindow(gothicDialog);\r
6699     gothicDialog = NULL;\r
6700   }\r
6701   if(variant != VariantNormal && gothicDialog == NULL) {\r
6702     title = lastTitle;\r
6703     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6704     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6705                  hwndMain, (DLGPROC)lpProc);\r
6706     FreeProcInstance(lpProc);\r
6707   }\r
6708 }\r
6709 #endif\r
6710 \r
6711 /*---------------------------------------------------------------------------*\\r
6712  *\r
6713  *  Ics Interaction console functions\r
6714  *\r
6715 \*---------------------------------------------------------------------------*/\r
6716 \r
6717 #define HISTORY_SIZE 64\r
6718 static char *history[HISTORY_SIZE];\r
6719 int histIn = 0, histP = 0;\r
6720 \r
6721 VOID\r
6722 SaveInHistory(char *cmd)\r
6723 {\r
6724   if (history[histIn] != NULL) {\r
6725     free(history[histIn]);\r
6726     history[histIn] = NULL;\r
6727   }\r
6728   if (*cmd == NULLCHAR) return;\r
6729   history[histIn] = StrSave(cmd);\r
6730   histIn = (histIn + 1) % HISTORY_SIZE;\r
6731   if (history[histIn] != NULL) {\r
6732     free(history[histIn]);\r
6733     history[histIn] = NULL;\r
6734   }\r
6735   histP = histIn;\r
6736 }\r
6737 \r
6738 char *\r
6739 PrevInHistory(char *cmd)\r
6740 {\r
6741   int newhp;\r
6742   if (histP == histIn) {\r
6743     if (history[histIn] != NULL) free(history[histIn]);\r
6744     history[histIn] = StrSave(cmd);\r
6745   }\r
6746   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6747   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6748   histP = newhp;\r
6749   return history[histP];\r
6750 }\r
6751 \r
6752 char *\r
6753 NextInHistory()\r
6754 {\r
6755   if (histP == histIn) return NULL;\r
6756   histP = (histP + 1) % HISTORY_SIZE;\r
6757   return history[histP];   \r
6758 }\r
6759 \r
6760 HMENU\r
6761 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6762 {\r
6763   HMENU hmenu, h;\r
6764   int i = 0;\r
6765   hmenu = LoadMenu(hInst, "TextMenu");\r
6766   h = GetSubMenu(hmenu, 0);\r
6767   while (e->item) {\r
6768     if (strcmp(e->item, "-") == 0) {\r
6769       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6770     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6771       int flags = MF_STRING, j = 0;\r
6772       if (e->item[0] == '|') {\r
6773         flags |= MF_MENUBARBREAK;\r
6774         j++;\r
6775       }\r
6776       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6777       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6778     }\r
6779     e++;\r
6780     i++;\r
6781   } \r
6782   return hmenu;\r
6783 }\r
6784 \r
6785 WNDPROC consoleTextWindowProc;\r
6786 \r
6787 void\r
6788 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6789 {\r
6790   char buf[MSG_SIZ], name[MSG_SIZ];\r
6791   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6792   CHARRANGE sel;\r
6793 \r
6794   if (!getname) {\r
6795     SetWindowText(hInput, command);\r
6796     if (immediate) {\r
6797       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6798     } else {\r
6799       sel.cpMin = 999999;\r
6800       sel.cpMax = 999999;\r
6801       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6802       SetFocus(hInput);\r
6803     }\r
6804     return;\r
6805   }    \r
6806   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6807   if (sel.cpMin == sel.cpMax) {\r
6808     /* Expand to surrounding word */\r
6809     TEXTRANGE tr;\r
6810     do {\r
6811       tr.chrg.cpMax = sel.cpMin;\r
6812       tr.chrg.cpMin = --sel.cpMin;\r
6813       if (sel.cpMin < 0) break;\r
6814       tr.lpstrText = name;\r
6815       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6816     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6817     sel.cpMin++;\r
6818 \r
6819     do {\r
6820       tr.chrg.cpMin = sel.cpMax;\r
6821       tr.chrg.cpMax = ++sel.cpMax;\r
6822       tr.lpstrText = name;\r
6823       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6824     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6825     sel.cpMax--;\r
6826 \r
6827     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6828       MessageBeep(MB_ICONEXCLAMATION);\r
6829       return;\r
6830     }\r
6831     tr.chrg = sel;\r
6832     tr.lpstrText = name;\r
6833     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6834   } else {\r
6835     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6836       MessageBeep(MB_ICONEXCLAMATION);\r
6837       return;\r
6838     }\r
6839     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6840   }\r
6841   if (immediate) {\r
6842     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6843     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6844     SetWindowText(hInput, buf);\r
6845     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6846   } else {\r
6847     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6848       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6849     SetWindowText(hInput, buf);\r
6850     sel.cpMin = 999999;\r
6851     sel.cpMax = 999999;\r
6852     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6853     SetFocus(hInput);\r
6854   }\r
6855 }\r
6856 \r
6857 LRESULT CALLBACK \r
6858 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6859 {\r
6860   HWND hInput;\r
6861   CHARRANGE sel;\r
6862 \r
6863   switch (message) {\r
6864   case WM_KEYDOWN:\r
6865     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6866     if(wParam=='R') return 0;\r
6867     switch (wParam) {\r
6868     case VK_PRIOR:\r
6869       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6870       return 0;\r
6871     case VK_NEXT:\r
6872       sel.cpMin = 999999;\r
6873       sel.cpMax = 999999;\r
6874       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6875       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6876       return 0;\r
6877     }\r
6878     break;\r
6879   case WM_CHAR:\r
6880    if(wParam != '\022') {\r
6881     if (wParam == '\t') {\r
6882       if (GetKeyState(VK_SHIFT) < 0) {\r
6883         /* shifted */\r
6884         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6885         if (buttonDesc[0].hwnd) {\r
6886           SetFocus(buttonDesc[0].hwnd);\r
6887         } else {\r
6888           SetFocus(hwndMain);\r
6889         }\r
6890       } else {\r
6891         /* unshifted */\r
6892         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6893       }\r
6894     } else {\r
6895       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6896       JAWS_DELETE( SetFocus(hInput); )\r
6897       SendMessage(hInput, message, wParam, lParam);\r
6898     }\r
6899     return 0;\r
6900    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6901    lParam = -1;\r
6902   case WM_RBUTTONDOWN:\r
6903     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6904       /* Move selection here if it was empty */\r
6905       POINT pt;\r
6906       pt.x = LOWORD(lParam);\r
6907       pt.y = HIWORD(lParam);\r
6908       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6909       if (sel.cpMin == sel.cpMax) {\r
6910         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6911         sel.cpMax = sel.cpMin;\r
6912         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6913       }\r
6914       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6915 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6916       POINT pt;\r
6917       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6918       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6919       if (sel.cpMin == sel.cpMax) {\r
6920         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6921         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6922       }\r
6923       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6924         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6925       }\r
6926       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6927       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6928       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6929       MenuPopup(hwnd, pt, hmenu, -1);\r
6930 }\r
6931     }\r
6932     return 0;\r
6933   case WM_RBUTTONUP:\r
6934     if (GetKeyState(VK_SHIFT) & ~1) {\r
6935       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6936         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6937     }\r
6938     return 0;\r
6939   case WM_PASTE:\r
6940     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6941     SetFocus(hInput);\r
6942     return SendMessage(hInput, message, wParam, lParam);\r
6943   case WM_MBUTTONDOWN:\r
6944     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6945   case WM_COMMAND:\r
6946     switch (LOWORD(wParam)) {\r
6947     case IDM_QuickPaste:\r
6948       {\r
6949         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6950         if (sel.cpMin == sel.cpMax) {\r
6951           MessageBeep(MB_ICONEXCLAMATION);\r
6952           return 0;\r
6953         }\r
6954         SendMessage(hwnd, WM_COPY, 0, 0);\r
6955         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6956         SendMessage(hInput, WM_PASTE, 0, 0);\r
6957         SetFocus(hInput);\r
6958         return 0;\r
6959       }\r
6960     case IDM_Cut:\r
6961       SendMessage(hwnd, WM_CUT, 0, 0);\r
6962       return 0;\r
6963     case IDM_Paste:\r
6964       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6965       return 0;\r
6966     case IDM_Copy:\r
6967       SendMessage(hwnd, WM_COPY, 0, 0);\r
6968       return 0;\r
6969     default:\r
6970       {\r
6971         int i = LOWORD(wParam) - IDM_CommandX;\r
6972         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6973             icsTextMenuEntry[i].command != NULL) {\r
6974           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6975                    icsTextMenuEntry[i].getname,\r
6976                    icsTextMenuEntry[i].immediate);\r
6977           return 0;\r
6978         }\r
6979       }\r
6980       break;\r
6981     }\r
6982     break;\r
6983   }\r
6984   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6985 }\r
6986 \r
6987 WNDPROC consoleInputWindowProc;\r
6988 \r
6989 LRESULT CALLBACK\r
6990 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6991 {\r
6992   char buf[MSG_SIZ];\r
6993   char *p;\r
6994   static BOOL sendNextChar = FALSE;\r
6995   static BOOL quoteNextChar = FALSE;\r
6996   InputSource *is = consoleInputSource;\r
6997   CHARFORMAT cf;\r
6998   CHARRANGE sel;\r
6999 \r
7000   switch (message) {\r
7001   case WM_CHAR:\r
7002     if (!appData.localLineEditing || sendNextChar) {\r
7003       is->buf[0] = (CHAR) wParam;\r
7004       is->count = 1;\r
7005       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7006       sendNextChar = FALSE;\r
7007       return 0;\r
7008     }\r
7009     if (quoteNextChar) {\r
7010       buf[0] = (char) wParam;\r
7011       buf[1] = NULLCHAR;\r
7012       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7013       quoteNextChar = FALSE;\r
7014       return 0;\r
7015     }\r
7016     switch (wParam) {\r
7017     case '\r':   /* Enter key */\r
7018       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7019       if (consoleEcho) SaveInHistory(is->buf);\r
7020       is->buf[is->count++] = '\n';\r
7021       is->buf[is->count] = NULLCHAR;\r
7022       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7023       if (consoleEcho) {\r
7024         ConsoleOutput(is->buf, is->count, TRUE);\r
7025       } else if (appData.localLineEditing) {\r
7026         ConsoleOutput("\n", 1, TRUE);\r
7027       }\r
7028       /* fall thru */\r
7029     case '\033': /* Escape key */\r
7030       SetWindowText(hwnd, "");\r
7031       cf.cbSize = sizeof(CHARFORMAT);\r
7032       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7033       if (consoleEcho) {\r
7034         cf.crTextColor = textAttribs[ColorNormal].color;\r
7035       } else {\r
7036         cf.crTextColor = COLOR_ECHOOFF;\r
7037       }\r
7038       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7039       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7040       return 0;\r
7041     case '\t':   /* Tab key */\r
7042       if (GetKeyState(VK_SHIFT) < 0) {\r
7043         /* shifted */\r
7044         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7045       } else {\r
7046         /* unshifted */\r
7047         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7048         if (buttonDesc[0].hwnd) {\r
7049           SetFocus(buttonDesc[0].hwnd);\r
7050         } else {\r
7051           SetFocus(hwndMain);\r
7052         }\r
7053       }\r
7054       return 0;\r
7055     case '\023': /* Ctrl+S */\r
7056       sendNextChar = TRUE;\r
7057       return 0;\r
7058     case '\021': /* Ctrl+Q */\r
7059       quoteNextChar = TRUE;\r
7060       return 0;\r
7061     JAWS_REPLAY\r
7062     default:\r
7063       break;\r
7064     }\r
7065     break;\r
7066   case WM_KEYDOWN:\r
7067     switch (wParam) {\r
7068     case VK_UP:\r
7069       GetWindowText(hwnd, buf, MSG_SIZ);\r
7070       p = PrevInHistory(buf);\r
7071       if (p != NULL) {\r
7072         SetWindowText(hwnd, p);\r
7073         sel.cpMin = 999999;\r
7074         sel.cpMax = 999999;\r
7075         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7076         return 0;\r
7077       }\r
7078       break;\r
7079     case VK_DOWN:\r
7080       p = NextInHistory();\r
7081       if (p != NULL) {\r
7082         SetWindowText(hwnd, p);\r
7083         sel.cpMin = 999999;\r
7084         sel.cpMax = 999999;\r
7085         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7086         return 0;\r
7087       }\r
7088       break;\r
7089     case VK_HOME:\r
7090     case VK_END:\r
7091       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7092       /* fall thru */\r
7093     case VK_PRIOR:\r
7094     case VK_NEXT:\r
7095       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7096       return 0;\r
7097     }\r
7098     break;\r
7099   case WM_MBUTTONDOWN:\r
7100     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7101       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7102     break;\r
7103   case WM_RBUTTONUP:\r
7104     if (GetKeyState(VK_SHIFT) & ~1) {\r
7105       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7106         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7107     } else {\r
7108       POINT pt;\r
7109       HMENU hmenu;\r
7110       hmenu = LoadMenu(hInst, "InputMenu");\r
7111       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7112       if (sel.cpMin == sel.cpMax) {\r
7113         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7114         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7115       }\r
7116       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7117         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7118       }\r
7119       pt.x = LOWORD(lParam);\r
7120       pt.y = HIWORD(lParam);\r
7121       MenuPopup(hwnd, pt, hmenu, -1);\r
7122     }\r
7123     return 0;\r
7124   case WM_COMMAND:\r
7125     switch (LOWORD(wParam)) { \r
7126     case IDM_Undo:\r
7127       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7128       return 0;\r
7129     case IDM_SelectAll:\r
7130       sel.cpMin = 0;\r
7131       sel.cpMax = -1; /*999999?*/\r
7132       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7133       return 0;\r
7134     case IDM_Cut:\r
7135       SendMessage(hwnd, WM_CUT, 0, 0);\r
7136       return 0;\r
7137     case IDM_Paste:\r
7138       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7139       return 0;\r
7140     case IDM_Copy:\r
7141       SendMessage(hwnd, WM_COPY, 0, 0);\r
7142       return 0;\r
7143     }\r
7144     break;\r
7145   }\r
7146   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7147 }\r
7148 \r
7149 #define CO_MAX  100000\r
7150 #define CO_TRIM   1000\r
7151 \r
7152 LRESULT CALLBACK\r
7153 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7154 {\r
7155   static SnapData sd;\r
7156   HWND hText, hInput;\r
7157   RECT rect;\r
7158   static int sizeX, sizeY;\r
7159   int newSizeX, newSizeY;\r
7160   MINMAXINFO *mmi;\r
7161   WORD wMask;\r
7162 \r
7163   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7164   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7165 \r
7166   switch (message) {\r
7167   case WM_NOTIFY:\r
7168     if (((NMHDR*)lParam)->code == EN_LINK)\r
7169     {\r
7170       ENLINK *pLink = (ENLINK*)lParam;\r
7171       if (pLink->msg == WM_LBUTTONUP)\r
7172       {\r
7173         TEXTRANGE tr;\r
7174 \r
7175         tr.chrg = pLink->chrg;\r
7176         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7177         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7178         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7179         free(tr.lpstrText);\r
7180       }\r
7181     }\r
7182     break;\r
7183   case WM_INITDIALOG: /* message: initialize dialog box */\r
7184     hwndConsole = hDlg;\r
7185     SetFocus(hInput);\r
7186     consoleTextWindowProc = (WNDPROC)\r
7187       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7188     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7189     consoleInputWindowProc = (WNDPROC)\r
7190       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7191     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7192     Colorize(ColorNormal, TRUE);\r
7193     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7194     ChangedConsoleFont();\r
7195     GetClientRect(hDlg, &rect);\r
7196     sizeX = rect.right;\r
7197     sizeY = rect.bottom;\r
7198     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7199         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7200       WINDOWPLACEMENT wp;\r
7201       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7202       wp.length = sizeof(WINDOWPLACEMENT);\r
7203       wp.flags = 0;\r
7204       wp.showCmd = SW_SHOW;\r
7205       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7206       wp.rcNormalPosition.left = wpConsole.x;\r
7207       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7208       wp.rcNormalPosition.top = wpConsole.y;\r
7209       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7210       SetWindowPlacement(hDlg, &wp);\r
7211     }\r
7212 \r
7213    // [HGM] Chessknight's change 2004-07-13\r
7214    else { /* Determine Defaults */\r
7215        WINDOWPLACEMENT wp;\r
7216        wpConsole.x = wpMain.width + 1;\r
7217        wpConsole.y = wpMain.y;\r
7218        wpConsole.width = screenWidth -  wpMain.width;\r
7219        wpConsole.height = wpMain.height;\r
7220        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7221        wp.length = sizeof(WINDOWPLACEMENT);\r
7222        wp.flags = 0;\r
7223        wp.showCmd = SW_SHOW;\r
7224        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7225        wp.rcNormalPosition.left = wpConsole.x;\r
7226        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7227        wp.rcNormalPosition.top = wpConsole.y;\r
7228        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7229        SetWindowPlacement(hDlg, &wp);\r
7230     }\r
7231 \r
7232    // Allow hText to highlight URLs and send notifications on them\r
7233    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7234    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7235    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7236    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7237 \r
7238     return FALSE;\r
7239 \r
7240   case WM_SETFOCUS:\r
7241     SetFocus(hInput);\r
7242     return 0;\r
7243 \r
7244   case WM_CLOSE:\r
7245     ExitEvent(0);\r
7246     /* not reached */\r
7247     break;\r
7248 \r
7249   case WM_SIZE:\r
7250     if (IsIconic(hDlg)) break;\r
7251     newSizeX = LOWORD(lParam);\r
7252     newSizeY = HIWORD(lParam);\r
7253     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7254       RECT rectText, rectInput;\r
7255       POINT pt;\r
7256       int newTextHeight, newTextWidth;\r
7257       GetWindowRect(hText, &rectText);\r
7258       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7259       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7260       if (newTextHeight < 0) {\r
7261         newSizeY += -newTextHeight;\r
7262         newTextHeight = 0;\r
7263       }\r
7264       SetWindowPos(hText, NULL, 0, 0,\r
7265         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7266       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7267       pt.x = rectInput.left;\r
7268       pt.y = rectInput.top + newSizeY - sizeY;\r
7269       ScreenToClient(hDlg, &pt);\r
7270       SetWindowPos(hInput, NULL, \r
7271         pt.x, pt.y, /* needs client coords */   \r
7272         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7273         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7274     }\r
7275     sizeX = newSizeX;\r
7276     sizeY = newSizeY;\r
7277     break;\r
7278 \r
7279   case WM_GETMINMAXINFO:\r
7280     /* Prevent resizing window too small */\r
7281     mmi = (MINMAXINFO *) lParam;\r
7282     mmi->ptMinTrackSize.x = 100;\r
7283     mmi->ptMinTrackSize.y = 100;\r
7284     break;\r
7285 \r
7286   /* [AS] Snapping */\r
7287   case WM_ENTERSIZEMOVE:\r
7288     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7289 \r
7290   case WM_SIZING:\r
7291     return OnSizing( &sd, hDlg, wParam, lParam );\r
7292 \r
7293   case WM_MOVING:\r
7294     return OnMoving( &sd, hDlg, wParam, lParam );\r
7295 \r
7296   case WM_EXITSIZEMOVE:\r
7297         UpdateICSWidth(hText);\r
7298     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7299   }\r
7300 \r
7301   return DefWindowProc(hDlg, message, wParam, lParam);\r
7302 }\r
7303 \r
7304 \r
7305 VOID\r
7306 ConsoleCreate()\r
7307 {\r
7308   HWND hCons;\r
7309   if (hwndConsole) return;\r
7310   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7311   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7312 }\r
7313 \r
7314 \r
7315 VOID\r
7316 ConsoleOutput(char* data, int length, int forceVisible)\r
7317 {\r
7318   HWND hText;\r
7319   int trim, exlen;\r
7320   char *p, *q;\r
7321   char buf[CO_MAX+1];\r
7322   POINT pEnd;\r
7323   RECT rect;\r
7324   static int delayLF = 0;\r
7325   CHARRANGE savesel, sel;\r
7326 \r
7327   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7328   p = data;\r
7329   q = buf;\r
7330   if (delayLF) {\r
7331     *q++ = '\r';\r
7332     *q++ = '\n';\r
7333     delayLF = 0;\r
7334   }\r
7335   while (length--) {\r
7336     if (*p == '\n') {\r
7337       if (*++p) {\r
7338         *q++ = '\r';\r
7339         *q++ = '\n';\r
7340       } else {\r
7341         delayLF = 1;\r
7342       }\r
7343     } else if (*p == '\007') {\r
7344        MyPlaySound(&sounds[(int)SoundBell]);\r
7345        p++;\r
7346     } else {\r
7347       *q++ = *p++;\r
7348     }\r
7349   }\r
7350   *q = NULLCHAR;\r
7351   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7352   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7353   /* Save current selection */\r
7354   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7355   exlen = GetWindowTextLength(hText);\r
7356   /* Find out whether current end of text is visible */\r
7357   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7358   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7359   /* Trim existing text if it's too long */\r
7360   if (exlen + (q - buf) > CO_MAX) {\r
7361     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7362     sel.cpMin = 0;\r
7363     sel.cpMax = trim;\r
7364     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7365     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7366     exlen -= trim;\r
7367     savesel.cpMin -= trim;\r
7368     savesel.cpMax -= trim;\r
7369     if (exlen < 0) exlen = 0;\r
7370     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7371     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7372   }\r
7373   /* Append the new text */\r
7374   sel.cpMin = exlen;\r
7375   sel.cpMax = exlen;\r
7376   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7377   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7378   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7379   if (forceVisible || exlen == 0 ||\r
7380       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7381        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7382     /* Scroll to make new end of text visible if old end of text\r
7383        was visible or new text is an echo of user typein */\r
7384     sel.cpMin = 9999999;\r
7385     sel.cpMax = 9999999;\r
7386     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7387     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7388     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7389     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7390   }\r
7391   if (savesel.cpMax == exlen || forceVisible) {\r
7392     /* Move insert point to new end of text if it was at the old\r
7393        end of text or if the new text is an echo of user typein */\r
7394     sel.cpMin = 9999999;\r
7395     sel.cpMax = 9999999;\r
7396     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7397   } else {\r
7398     /* Restore previous selection */\r
7399     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7400   }\r
7401   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7402 }\r
7403 \r
7404 /*---------*/\r
7405 \r
7406 \r
7407 void\r
7408 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7409 {\r
7410   char buf[100];\r
7411   char *str;\r
7412   COLORREF oldFg, oldBg;\r
7413   HFONT oldFont;\r
7414   RECT rect;\r
7415 \r
7416   if(copyNumber > 1)
7417     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7418 \r
7419   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7420   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7421   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7422 \r
7423   rect.left = x;\r
7424   rect.right = x + squareSize;\r
7425   rect.top  = y;\r
7426   rect.bottom = y + squareSize;\r
7427   str = buf;\r
7428 \r
7429   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7430                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7431              y, ETO_CLIPPED|ETO_OPAQUE,\r
7432              &rect, str, strlen(str), NULL);\r
7433 \r
7434   (void) SetTextColor(hdc, oldFg);\r
7435   (void) SetBkColor(hdc, oldBg);\r
7436   (void) SelectObject(hdc, oldFont);\r
7437 }\r
7438 \r
7439 void\r
7440 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7441               RECT *rect, char *color, char *flagFell)\r
7442 {\r
7443   char buf[100];\r
7444   char *str;\r
7445   COLORREF oldFg, oldBg;\r
7446   HFONT oldFont;\r
7447 \r
7448   if (appData.clockMode) {\r
7449     if (tinyLayout)\r
7450       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7451     else\r
7452       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7453     str = buf;\r
7454   } else {\r
7455     str = color;\r
7456   }\r
7457 \r
7458   if (highlight) {\r
7459     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7460     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7461   } else {\r
7462     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7463     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7464   }\r
7465   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7466 \r
7467   JAWS_SILENCE\r
7468 \r
7469   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7470              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7471              rect, str, strlen(str), NULL);\r
7472   if(logoHeight > 0 && appData.clockMode) {\r
7473       RECT r;\r
7474       str += strlen(color)+2;\r
7475       r.top = rect->top + logoHeight/2;\r
7476       r.left = rect->left;\r
7477       r.right = rect->right;\r
7478       r.bottom = rect->bottom;\r
7479       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7480                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7481                  &r, str, strlen(str), NULL);\r
7482   }\r
7483   (void) SetTextColor(hdc, oldFg);\r
7484   (void) SetBkColor(hdc, oldBg);\r
7485   (void) SelectObject(hdc, oldFont);\r
7486 }\r
7487 \r
7488 \r
7489 int\r
7490 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7491            OVERLAPPED *ovl)\r
7492 {\r
7493   int ok, err;\r
7494 \r
7495   /* [AS]  */\r
7496   if( count <= 0 ) {\r
7497     if (appData.debugMode) {\r
7498       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7499     }\r
7500 \r
7501     return ERROR_INVALID_USER_BUFFER;\r
7502   }\r
7503 \r
7504   ResetEvent(ovl->hEvent);\r
7505   ovl->Offset = ovl->OffsetHigh = 0;\r
7506   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7507   if (ok) {\r
7508     err = NO_ERROR;\r
7509   } else {\r
7510     err = GetLastError();\r
7511     if (err == ERROR_IO_PENDING) {\r
7512       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7513       if (ok)\r
7514         err = NO_ERROR;\r
7515       else\r
7516         err = GetLastError();\r
7517     }\r
7518   }\r
7519   return err;\r
7520 }\r
7521 \r
7522 int\r
7523 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7524             OVERLAPPED *ovl)\r
7525 {\r
7526   int ok, err;\r
7527 \r
7528   ResetEvent(ovl->hEvent);\r
7529   ovl->Offset = ovl->OffsetHigh = 0;\r
7530   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7531   if (ok) {\r
7532     err = NO_ERROR;\r
7533   } else {\r
7534     err = GetLastError();\r
7535     if (err == ERROR_IO_PENDING) {\r
7536       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7537       if (ok)\r
7538         err = NO_ERROR;\r
7539       else\r
7540         err = GetLastError();\r
7541     }\r
7542   }\r
7543   return err;\r
7544 }\r
7545 \r
7546 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7547 void CheckForInputBufferFull( InputSource * is )\r
7548 {\r
7549     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7550         /* Look for end of line */\r
7551         char * p = is->buf;\r
7552         \r
7553         while( p < is->next && *p != '\n' ) {\r
7554             p++;\r
7555         }\r
7556 \r
7557         if( p >= is->next ) {\r
7558             if (appData.debugMode) {\r
7559                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7560             }\r
7561 \r
7562             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7563             is->count = (DWORD) -1;\r
7564             is->next = is->buf;\r
7565         }\r
7566     }\r
7567 }\r
7568 \r
7569 DWORD\r
7570 InputThread(LPVOID arg)\r
7571 {\r
7572   InputSource *is;\r
7573   OVERLAPPED ovl;\r
7574 \r
7575   is = (InputSource *) arg;\r
7576   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7577   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7578   while (is->hThread != NULL) {\r
7579     is->error = DoReadFile(is->hFile, is->next,\r
7580                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7581                            &is->count, &ovl);\r
7582     if (is->error == NO_ERROR) {\r
7583       is->next += is->count;\r
7584     } else {\r
7585       if (is->error == ERROR_BROKEN_PIPE) {\r
7586         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7587         is->count = 0;\r
7588       } else {\r
7589         is->count = (DWORD) -1;\r
7590         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7591         break; \r
7592       }\r
7593     }\r
7594 \r
7595     CheckForInputBufferFull( is );\r
7596 \r
7597     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7598 \r
7599     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7600 \r
7601     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7602   }\r
7603 \r
7604   CloseHandle(ovl.hEvent);\r
7605   CloseHandle(is->hFile);\r
7606 \r
7607   if (appData.debugMode) {\r
7608     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7609   }\r
7610 \r
7611   return 0;\r
7612 }\r
7613 \r
7614 \r
7615 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7616 DWORD\r
7617 NonOvlInputThread(LPVOID arg)\r
7618 {\r
7619   InputSource *is;\r
7620   char *p, *q;\r
7621   int i;\r
7622   char prev;\r
7623 \r
7624   is = (InputSource *) arg;\r
7625   while (is->hThread != NULL) {\r
7626     is->error = ReadFile(is->hFile, is->next,\r
7627                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7628                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7629     if (is->error == NO_ERROR) {\r
7630       /* Change CRLF to LF */\r
7631       if (is->next > is->buf) {\r
7632         p = is->next - 1;\r
7633         i = is->count + 1;\r
7634       } else {\r
7635         p = is->next;\r
7636         i = is->count;\r
7637       }\r
7638       q = p;\r
7639       prev = NULLCHAR;\r
7640       while (i > 0) {\r
7641         if (prev == '\r' && *p == '\n') {\r
7642           *(q-1) = '\n';\r
7643           is->count--;\r
7644         } else { \r
7645           *q++ = *p;\r
7646         }\r
7647         prev = *p++;\r
7648         i--;\r
7649       }\r
7650       *q = NULLCHAR;\r
7651       is->next = q;\r
7652     } else {\r
7653       if (is->error == ERROR_BROKEN_PIPE) {\r
7654         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7655         is->count = 0; \r
7656       } else {\r
7657         is->count = (DWORD) -1;\r
7658       }\r
7659     }\r
7660 \r
7661     CheckForInputBufferFull( is );\r
7662 \r
7663     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7664 \r
7665     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7666 \r
7667     if (is->count < 0) break;  /* Quit on error */\r
7668   }\r
7669   CloseHandle(is->hFile);\r
7670   return 0;\r
7671 }\r
7672 \r
7673 DWORD\r
7674 SocketInputThread(LPVOID arg)\r
7675 {\r
7676   InputSource *is;\r
7677 \r
7678   is = (InputSource *) arg;\r
7679   while (is->hThread != NULL) {\r
7680     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7681     if ((int)is->count == SOCKET_ERROR) {\r
7682       is->count = (DWORD) -1;\r
7683       is->error = WSAGetLastError();\r
7684     } else {\r
7685       is->error = NO_ERROR;\r
7686       is->next += is->count;\r
7687       if (is->count == 0 && is->second == is) {\r
7688         /* End of file on stderr; quit with no message */\r
7689         break;\r
7690       }\r
7691     }\r
7692     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7693 \r
7694     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7695 \r
7696     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7697   }\r
7698   return 0;\r
7699 }\r
7700 \r
7701 VOID\r
7702 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7703 {\r
7704   InputSource *is;\r
7705 \r
7706   is = (InputSource *) lParam;\r
7707   if (is->lineByLine) {\r
7708     /* Feed in lines one by one */\r
7709     char *p = is->buf;\r
7710     char *q = p;\r
7711     while (q < is->next) {\r
7712       if (*q++ == '\n') {\r
7713         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7714         p = q;\r
7715       }\r
7716     }\r
7717     \r
7718     /* Move any partial line to the start of the buffer */\r
7719     q = is->buf;\r
7720     while (p < is->next) {\r
7721       *q++ = *p++;\r
7722     }\r
7723     is->next = q;\r
7724 \r
7725     if (is->error != NO_ERROR || is->count == 0) {\r
7726       /* Notify backend of the error.  Note: If there was a partial\r
7727          line at the end, it is not flushed through. */\r
7728       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7729     }\r
7730   } else {\r
7731     /* Feed in the whole chunk of input at once */\r
7732     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7733     is->next = is->buf;\r
7734   }\r
7735 }\r
7736 \r
7737 /*---------------------------------------------------------------------------*\\r
7738  *\r
7739  *  Menu enables. Used when setting various modes.\r
7740  *\r
7741 \*---------------------------------------------------------------------------*/\r
7742 \r
7743 typedef struct {\r
7744   int item;\r
7745   int flags;\r
7746 } Enables;\r
7747 \r
7748 VOID\r
7749 GreyRevert(Boolean grey)\r
7750 { // [HGM] vari: for retracting variations in local mode\r
7751   HMENU hmenu = GetMenu(hwndMain);\r
7752   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7753   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7754 }\r
7755 \r
7756 VOID\r
7757 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7758 {\r
7759   while (enab->item > 0) {\r
7760     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7761     enab++;\r
7762   }\r
7763 }\r
7764 \r
7765 Enables gnuEnables[] = {\r
7766   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7779 \r
7780   // Needed to switch from ncp to GNU mode on Engine Load\r
7781   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7782   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7783   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7784   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7785   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7786   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7787   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7788   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7789   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7790   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7791   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7792   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7793   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7794   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7795   { -1, -1 }\r
7796 };\r
7797 \r
7798 Enables icsEnables[] = {\r
7799   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7818   { -1, -1 }\r
7819 };\r
7820 \r
7821 #if ZIPPY\r
7822 Enables zippyEnables[] = {\r
7823   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7824   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7827   { -1, -1 }\r
7828 };\r
7829 #endif\r
7830 \r
7831 Enables ncpEnables[] = {\r
7832   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7841   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7842   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7854   { -1, -1 }\r
7855 };\r
7856 \r
7857 Enables trainingOnEnables[] = {\r
7858   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7867   { -1, -1 }\r
7868 };\r
7869 \r
7870 Enables trainingOffEnables[] = {\r
7871   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7880   { -1, -1 }\r
7881 };\r
7882 \r
7883 /* These modify either ncpEnables or gnuEnables */\r
7884 Enables cmailEnables[] = {\r
7885   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7886   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7887   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7888   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7890   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7892   { -1, -1 }\r
7893 };\r
7894 \r
7895 Enables machineThinkingEnables[] = {\r
7896   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7909 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7912   { -1, -1 }\r
7913 };\r
7914 \r
7915 Enables userThinkingEnables[] = {\r
7916   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7929 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7932   { -1, -1 }\r
7933 };\r
7934 \r
7935 /*---------------------------------------------------------------------------*\\r
7936  *\r
7937  *  Front-end interface functions exported by XBoard.\r
7938  *  Functions appear in same order as prototypes in frontend.h.\r
7939  * \r
7940 \*---------------------------------------------------------------------------*/\r
7941 VOID\r
7942 CheckMark(UINT item, int state)\r
7943 {\r
7944     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7945 }\r
7946 \r
7947 VOID\r
7948 ModeHighlight()\r
7949 {\r
7950   static UINT prevChecked = 0;\r
7951   static int prevPausing = 0;\r
7952   UINT nowChecked;\r
7953 \r
7954   if (pausing != prevPausing) {\r
7955     prevPausing = pausing;\r
7956     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7957                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7958     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7959   }\r
7960 \r
7961   switch (gameMode) {\r
7962   case BeginningOfGame:\r
7963     if (appData.icsActive)\r
7964       nowChecked = IDM_IcsClient;\r
7965     else if (appData.noChessProgram)\r
7966       nowChecked = IDM_EditGame;\r
7967     else\r
7968       nowChecked = IDM_MachineBlack;\r
7969     break;\r
7970   case MachinePlaysBlack:\r
7971     nowChecked = IDM_MachineBlack;\r
7972     break;\r
7973   case MachinePlaysWhite:\r
7974     nowChecked = IDM_MachineWhite;\r
7975     break;\r
7976   case TwoMachinesPlay:\r
7977     nowChecked = IDM_TwoMachines;\r
7978     break;\r
7979   case AnalyzeMode:\r
7980     nowChecked = IDM_AnalysisMode;\r
7981     break;\r
7982   case AnalyzeFile:\r
7983     nowChecked = IDM_AnalyzeFile;\r
7984     break;\r
7985   case EditGame:\r
7986     nowChecked = IDM_EditGame;\r
7987     break;\r
7988   case PlayFromGameFile:\r
7989     nowChecked = IDM_LoadGame;\r
7990     break;\r
7991   case EditPosition:\r
7992     nowChecked = IDM_EditPosition;\r
7993     break;\r
7994   case Training:\r
7995     nowChecked = IDM_Training;\r
7996     break;\r
7997   case IcsPlayingWhite:\r
7998   case IcsPlayingBlack:\r
7999   case IcsObserving:\r
8000   case IcsIdle:\r
8001     nowChecked = IDM_IcsClient;\r
8002     break;\r
8003   default:\r
8004   case EndOfGame:\r
8005     nowChecked = 0;\r
8006     break;\r
8007   }\r
8008   CheckMark(prevChecked, MF_UNCHECKED);\r
8009   CheckMark(nowChecked, MF_CHECKED);\r
8010   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8011 \r
8012   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8013     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8014                           MF_BYCOMMAND|MF_ENABLED);\r
8015   } else {\r
8016     (void) EnableMenuItem(GetMenu(hwndMain), \r
8017                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8018   }\r
8019 \r
8020   prevChecked = nowChecked;\r
8021 \r
8022   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8023   if (appData.icsActive) {\r
8024        if (appData.icsEngineAnalyze) {\r
8025                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8026        } else {\r
8027                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8028        }\r
8029   }\r
8030   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8031 }\r
8032 \r
8033 VOID\r
8034 SetICSMode()\r
8035 {\r
8036   HMENU hmenu = GetMenu(hwndMain);\r
8037   SetMenuEnables(hmenu, icsEnables);\r
8038   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8039     MF_BYCOMMAND|MF_ENABLED);\r
8040 #if ZIPPY\r
8041   if (appData.zippyPlay) {\r
8042     SetMenuEnables(hmenu, zippyEnables);\r
8043     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8044          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8045           MF_BYCOMMAND|MF_ENABLED);\r
8046   }\r
8047 #endif\r
8048 }\r
8049 \r
8050 VOID\r
8051 SetGNUMode()\r
8052 {\r
8053   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8054 }\r
8055 \r
8056 VOID\r
8057 SetNCPMode()\r
8058 {\r
8059   HMENU hmenu = GetMenu(hwndMain);\r
8060   SetMenuEnables(hmenu, ncpEnables);\r
8061     DrawMenuBar(hwndMain);\r
8062 }\r
8063 \r
8064 VOID\r
8065 SetCmailMode()\r
8066 {\r
8067   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8068 }\r
8069 \r
8070 VOID \r
8071 SetTrainingModeOn()\r
8072 {\r
8073   int i;\r
8074   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8075   for (i = 0; i < N_BUTTONS; i++) {\r
8076     if (buttonDesc[i].hwnd != NULL)\r
8077       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8078   }\r
8079   CommentPopDown();\r
8080 }\r
8081 \r
8082 VOID SetTrainingModeOff()\r
8083 {\r
8084   int i;\r
8085   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8086   for (i = 0; i < N_BUTTONS; i++) {\r
8087     if (buttonDesc[i].hwnd != NULL)\r
8088       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8089   }\r
8090 }\r
8091 \r
8092 \r
8093 VOID\r
8094 SetUserThinkingEnables()\r
8095 {\r
8096   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8097 }\r
8098 \r
8099 VOID\r
8100 SetMachineThinkingEnables()\r
8101 {\r
8102   HMENU hMenu = GetMenu(hwndMain);\r
8103   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8104 \r
8105   SetMenuEnables(hMenu, machineThinkingEnables);\r
8106 \r
8107   if (gameMode == MachinePlaysBlack) {\r
8108     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8109   } else if (gameMode == MachinePlaysWhite) {\r
8110     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8111   } else if (gameMode == TwoMachinesPlay) {\r
8112     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8113   }\r
8114 }\r
8115 \r
8116 \r
8117 VOID\r
8118 DisplayTitle(char *str)\r
8119 {\r
8120   char title[MSG_SIZ], *host;\r
8121   if (str[0] != NULLCHAR) {\r
8122     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8123   } else if (appData.icsActive) {\r
8124     if (appData.icsCommPort[0] != NULLCHAR)\r
8125       host = "ICS";\r
8126     else \r
8127       host = appData.icsHost;\r
8128       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8129   } else if (appData.noChessProgram) {\r
8130     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8131   } else {\r
8132     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8133     strcat(title, ": ");\r
8134     strcat(title, first.tidy);\r
8135   }\r
8136   SetWindowText(hwndMain, title);\r
8137 }\r
8138 \r
8139 \r
8140 VOID\r
8141 DisplayMessage(char *str1, char *str2)\r
8142 {\r
8143   HDC hdc;\r
8144   HFONT oldFont;\r
8145   int remain = MESSAGE_TEXT_MAX - 1;\r
8146   int len;\r
8147 \r
8148   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8149   messageText[0] = NULLCHAR;\r
8150   if (*str1) {\r
8151     len = strlen(str1);\r
8152     if (len > remain) len = remain;\r
8153     strncpy(messageText, str1, len);\r
8154     messageText[len] = NULLCHAR;\r
8155     remain -= len;\r
8156   }\r
8157   if (*str2 && remain >= 2) {\r
8158     if (*str1) {\r
8159       strcat(messageText, "  ");\r
8160       remain -= 2;\r
8161     }\r
8162     len = strlen(str2);\r
8163     if (len > remain) len = remain;\r
8164     strncat(messageText, str2, len);\r
8165   }\r
8166   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8167   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8168 \r
8169   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8170 \r
8171   SAYMACHINEMOVE();\r
8172 \r
8173   hdc = GetDC(hwndMain);\r
8174   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8175   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8176              &messageRect, messageText, strlen(messageText), NULL);\r
8177   (void) SelectObject(hdc, oldFont);\r
8178   (void) ReleaseDC(hwndMain, hdc);\r
8179 }\r
8180 \r
8181 VOID\r
8182 DisplayError(char *str, int error)\r
8183 {\r
8184   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8185   int len;\r
8186 \r
8187   if (error == 0) {\r
8188     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8189   } else {\r
8190     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8191                         NULL, error, LANG_NEUTRAL,\r
8192                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8193     if (len > 0) {\r
8194       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8195     } else {\r
8196       ErrorMap *em = errmap;\r
8197       while (em->err != 0 && em->err != error) em++;\r
8198       if (em->err != 0) {\r
8199         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8200       } else {\r
8201         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8202       }\r
8203     }\r
8204   }\r
8205   \r
8206   ErrorPopUp(_("Error"), buf);\r
8207 }\r
8208 \r
8209 \r
8210 VOID\r
8211 DisplayMoveError(char *str)\r
8212 {\r
8213   fromX = fromY = -1;\r
8214   ClearHighlights();\r
8215   DrawPosition(FALSE, NULL);\r
8216   if (appData.popupMoveErrors) {\r
8217     ErrorPopUp(_("Error"), str);\r
8218   } else {\r
8219     DisplayMessage(str, "");\r
8220     moveErrorMessageUp = TRUE;\r
8221   }\r
8222 }\r
8223 \r
8224 VOID\r
8225 DisplayFatalError(char *str, int error, int exitStatus)\r
8226 {\r
8227   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8228   int len;\r
8229   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8230 \r
8231   if (error != 0) {\r
8232     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8233                         NULL, error, LANG_NEUTRAL,\r
8234                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8235     if (len > 0) {\r
8236       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8237     } else {\r
8238       ErrorMap *em = errmap;\r
8239       while (em->err != 0 && em->err != error) em++;\r
8240       if (em->err != 0) {\r
8241         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8242       } else {\r
8243         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8244       }\r
8245     }\r
8246     str = buf;\r
8247   }\r
8248   if (appData.debugMode) {\r
8249     fprintf(debugFP, "%s: %s\n", label, str);\r
8250   }\r
8251   if (appData.popupExitMessage) {\r
8252     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8253                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8254   }\r
8255   ExitEvent(exitStatus);\r
8256 }\r
8257 \r
8258 \r
8259 VOID\r
8260 DisplayInformation(char *str)\r
8261 {\r
8262   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8263 }\r
8264 \r
8265 \r
8266 VOID\r
8267 DisplayNote(char *str)\r
8268 {\r
8269   ErrorPopUp(_("Note"), str);\r
8270 }\r
8271 \r
8272 \r
8273 typedef struct {\r
8274   char *title, *question, *replyPrefix;\r
8275   ProcRef pr;\r
8276 } QuestionParams;\r
8277 \r
8278 LRESULT CALLBACK\r
8279 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8280 {\r
8281   static QuestionParams *qp;\r
8282   char reply[MSG_SIZ];\r
8283   int len, err;\r
8284 \r
8285   switch (message) {\r
8286   case WM_INITDIALOG:\r
8287     qp = (QuestionParams *) lParam;\r
8288     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8289     Translate(hDlg, DLG_Question);\r
8290     SetWindowText(hDlg, qp->title);\r
8291     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8292     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8293     return FALSE;\r
8294 \r
8295   case WM_COMMAND:\r
8296     switch (LOWORD(wParam)) {\r
8297     case IDOK:\r
8298       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8299       if (*reply) strcat(reply, " ");\r
8300       len = strlen(reply);\r
8301       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8302       strcat(reply, "\n");\r
8303       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8304       EndDialog(hDlg, TRUE);\r
8305       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8306       return TRUE;\r
8307     case IDCANCEL:\r
8308       EndDialog(hDlg, FALSE);\r
8309       return TRUE;\r
8310     default:\r
8311       break;\r
8312     }\r
8313     break;\r
8314   }\r
8315   return FALSE;\r
8316 }\r
8317 \r
8318 VOID\r
8319 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8320 {\r
8321     QuestionParams qp;\r
8322     FARPROC lpProc;\r
8323     \r
8324     qp.title = title;\r
8325     qp.question = question;\r
8326     qp.replyPrefix = replyPrefix;\r
8327     qp.pr = pr;\r
8328     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8329     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8330       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8331     FreeProcInstance(lpProc);\r
8332 }\r
8333 \r
8334 /* [AS] Pick FRC position */\r
8335 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8336 {\r
8337     static int * lpIndexFRC;\r
8338     BOOL index_is_ok;\r
8339     char buf[16];\r
8340 \r
8341     switch( message )\r
8342     {\r
8343     case WM_INITDIALOG:\r
8344         lpIndexFRC = (int *) lParam;\r
8345 \r
8346         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8347         Translate(hDlg, DLG_NewGameFRC);\r
8348 \r
8349         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8350         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8351         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8352         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8353 \r
8354         break;\r
8355 \r
8356     case WM_COMMAND:\r
8357         switch( LOWORD(wParam) ) {\r
8358         case IDOK:\r
8359             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8360             EndDialog( hDlg, 0 );\r
8361             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8362             return TRUE;\r
8363         case IDCANCEL:\r
8364             EndDialog( hDlg, 1 );   \r
8365             return TRUE;\r
8366         case IDC_NFG_Edit:\r
8367             if( HIWORD(wParam) == EN_CHANGE ) {\r
8368                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8369 \r
8370                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8371             }\r
8372             return TRUE;\r
8373         case IDC_NFG_Random:\r
8374           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8375             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8376             return TRUE;\r
8377         }\r
8378 \r
8379         break;\r
8380     }\r
8381 \r
8382     return FALSE;\r
8383 }\r
8384 \r
8385 int NewGameFRC()\r
8386 {\r
8387     int result;\r
8388     int index = appData.defaultFrcPosition;\r
8389     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8390 \r
8391     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8392 \r
8393     if( result == 0 ) {\r
8394         appData.defaultFrcPosition = index;\r
8395     }\r
8396 \r
8397     return result;\r
8398 }\r
8399 \r
8400 /* [AS] Game list options. Refactored by HGM */\r
8401 \r
8402 HWND gameListOptionsDialog;\r
8403 \r
8404 // low-level front-end: clear text edit / list widget\r
8405 void\r
8406 GLT_ClearList()\r
8407 {\r
8408     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8409 }\r
8410 \r
8411 // low-level front-end: clear text edit / list widget\r
8412 void\r
8413 GLT_DeSelectList()\r
8414 {\r
8415     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8416 }\r
8417 \r
8418 // low-level front-end: append line to text edit / list widget\r
8419 void\r
8420 GLT_AddToList( char *name )\r
8421 {\r
8422     if( name != 0 ) {\r
8423             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8424     }\r
8425 }\r
8426 \r
8427 // low-level front-end: get line from text edit / list widget\r
8428 Boolean\r
8429 GLT_GetFromList( int index, char *name )\r
8430 {\r
8431     if( name != 0 ) {\r
8432             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8433                 return TRUE;\r
8434     }\r
8435     return FALSE;\r
8436 }\r
8437 \r
8438 void GLT_MoveSelection( HWND hDlg, int delta )\r
8439 {\r
8440     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8441     int idx2 = idx1 + delta;\r
8442     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8443 \r
8444     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8445         char buf[128];\r
8446 \r
8447         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8448         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8449         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8450         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8451     }\r
8452 }\r
8453 \r
8454 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8455 {\r
8456     switch( message )\r
8457     {\r
8458     case WM_INITDIALOG:\r
8459         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8460         \r
8461         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8462         Translate(hDlg, DLG_GameListOptions);\r
8463 \r
8464         /* Initialize list */\r
8465         GLT_TagsToList( lpUserGLT );\r
8466 \r
8467         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8468 \r
8469         break;\r
8470 \r
8471     case WM_COMMAND:\r
8472         switch( LOWORD(wParam) ) {\r
8473         case IDOK:\r
8474             GLT_ParseList();\r
8475             EndDialog( hDlg, 0 );\r
8476             return TRUE;\r
8477         case IDCANCEL:\r
8478             EndDialog( hDlg, 1 );\r
8479             return TRUE;\r
8480 \r
8481         case IDC_GLT_Default:\r
8482             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8483             return TRUE;\r
8484 \r
8485         case IDC_GLT_Restore:\r
8486             GLT_TagsToList( appData.gameListTags );\r
8487             return TRUE;\r
8488 \r
8489         case IDC_GLT_Up:\r
8490             GLT_MoveSelection( hDlg, -1 );\r
8491             return TRUE;\r
8492 \r
8493         case IDC_GLT_Down:\r
8494             GLT_MoveSelection( hDlg, +1 );\r
8495             return TRUE;\r
8496         }\r
8497 \r
8498         break;\r
8499     }\r
8500 \r
8501     return FALSE;\r
8502 }\r
8503 \r
8504 int GameListOptions()\r
8505 {\r
8506     int result;\r
8507     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8508 \r
8509       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8510 \r
8511     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8512 \r
8513     if( result == 0 ) {\r
8514         /* [AS] Memory leak here! */\r
8515         appData.gameListTags = strdup( lpUserGLT ); \r
8516     }\r
8517 \r
8518     return result;\r
8519 }\r
8520 \r
8521 VOID\r
8522 DisplayIcsInteractionTitle(char *str)\r
8523 {\r
8524   char consoleTitle[MSG_SIZ];\r
8525 \r
8526     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8527   SetWindowText(hwndConsole, consoleTitle);\r
8528 }\r
8529 \r
8530 void\r
8531 DrawPosition(int fullRedraw, Board board)\r
8532 {\r
8533   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8534 }\r
8535 \r
8536 void NotifyFrontendLogin()\r
8537 {\r
8538         if (hwndConsole)\r
8539                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8540 }\r
8541 \r
8542 VOID\r
8543 ResetFrontEnd()\r
8544 {\r
8545   fromX = fromY = -1;\r
8546   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8547     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8548     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8549     dragInfo.lastpos = dragInfo.pos;\r
8550     dragInfo.start.x = dragInfo.start.y = -1;\r
8551     dragInfo.from = dragInfo.start;\r
8552     ReleaseCapture();\r
8553     DrawPosition(TRUE, NULL);\r
8554   }\r
8555   TagsPopDown();\r
8556 }\r
8557 \r
8558 \r
8559 VOID\r
8560 CommentPopUp(char *title, char *str)\r
8561 {\r
8562   HWND hwnd = GetActiveWindow();\r
8563   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8564   SAY(str);\r
8565   SetActiveWindow(hwnd);\r
8566 }\r
8567 \r
8568 VOID\r
8569 CommentPopDown(void)\r
8570 {\r
8571   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8572   if (commentDialog) {\r
8573     ShowWindow(commentDialog, SW_HIDE);\r
8574   }\r
8575   commentUp = FALSE;\r
8576 }\r
8577 \r
8578 VOID\r
8579 EditCommentPopUp(int index, char *title, char *str)\r
8580 {\r
8581   EitherCommentPopUp(index, title, str, TRUE);\r
8582 }\r
8583 \r
8584 \r
8585 VOID\r
8586 RingBell()\r
8587 {\r
8588   MyPlaySound(&sounds[(int)SoundMove]);\r
8589 }\r
8590 \r
8591 VOID PlayIcsWinSound()\r
8592 {\r
8593   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8594 }\r
8595 \r
8596 VOID PlayIcsLossSound()\r
8597 {\r
8598   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8599 }\r
8600 \r
8601 VOID PlayIcsDrawSound()\r
8602 {\r
8603   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8604 }\r
8605 \r
8606 VOID PlayIcsUnfinishedSound()\r
8607 {\r
8608   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8609 }\r
8610 \r
8611 VOID\r
8612 PlayAlarmSound()\r
8613 {\r
8614   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8615 }\r
8616 \r
8617 VOID\r
8618 PlayTellSound()\r
8619 {\r
8620   MyPlaySound(&textAttribs[ColorTell].sound);\r
8621 }\r
8622 \r
8623 \r
8624 VOID\r
8625 EchoOn()\r
8626 {\r
8627   HWND hInput;\r
8628   consoleEcho = TRUE;\r
8629   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8630   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8631   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8632 }\r
8633 \r
8634 \r
8635 VOID\r
8636 EchoOff()\r
8637 {\r
8638   CHARFORMAT cf;\r
8639   HWND hInput;\r
8640   consoleEcho = FALSE;\r
8641   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8642   /* This works OK: set text and background both to the same color */\r
8643   cf = consoleCF;\r
8644   cf.crTextColor = COLOR_ECHOOFF;\r
8645   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8646   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8647 }\r
8648 \r
8649 /* No Raw()...? */\r
8650 \r
8651 void Colorize(ColorClass cc, int continuation)\r
8652 {\r
8653   currentColorClass = cc;\r
8654   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8655   consoleCF.crTextColor = textAttribs[cc].color;\r
8656   consoleCF.dwEffects = textAttribs[cc].effects;\r
8657   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8658 }\r
8659 \r
8660 char *\r
8661 UserName()\r
8662 {\r
8663   static char buf[MSG_SIZ];\r
8664   DWORD bufsiz = MSG_SIZ;\r
8665 \r
8666   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8667         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8668   }\r
8669   if (!GetUserName(buf, &bufsiz)) {\r
8670     /*DisplayError("Error getting user name", GetLastError());*/\r
8671     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8672   }\r
8673   return buf;\r
8674 }\r
8675 \r
8676 char *\r
8677 HostName()\r
8678 {\r
8679   static char buf[MSG_SIZ];\r
8680   DWORD bufsiz = MSG_SIZ;\r
8681 \r
8682   if (!GetComputerName(buf, &bufsiz)) {\r
8683     /*DisplayError("Error getting host name", GetLastError());*/\r
8684     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8685   }\r
8686   return buf;\r
8687 }\r
8688 \r
8689 \r
8690 int\r
8691 ClockTimerRunning()\r
8692 {\r
8693   return clockTimerEvent != 0;\r
8694 }\r
8695 \r
8696 int\r
8697 StopClockTimer()\r
8698 {\r
8699   if (clockTimerEvent == 0) return FALSE;\r
8700   KillTimer(hwndMain, clockTimerEvent);\r
8701   clockTimerEvent = 0;\r
8702   return TRUE;\r
8703 }\r
8704 \r
8705 void\r
8706 StartClockTimer(long millisec)\r
8707 {\r
8708   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8709                              (UINT) millisec, NULL);\r
8710 }\r
8711 \r
8712 void\r
8713 DisplayWhiteClock(long timeRemaining, int highlight)\r
8714 {\r
8715   HDC hdc;\r
8716   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8717 \r
8718   if(appData.noGUI) return;\r
8719   hdc = GetDC(hwndMain);\r
8720   if (!IsIconic(hwndMain)) {\r
8721     DisplayAClock(hdc, timeRemaining, highlight, \r
8722                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8723   }\r
8724   if (highlight && iconCurrent == iconBlack) {\r
8725     iconCurrent = iconWhite;\r
8726     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8727     if (IsIconic(hwndMain)) {\r
8728       DrawIcon(hdc, 2, 2, iconCurrent);\r
8729     }\r
8730   }\r
8731   (void) ReleaseDC(hwndMain, hdc);\r
8732   if (hwndConsole)\r
8733     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8734 }\r
8735 \r
8736 void\r
8737 DisplayBlackClock(long timeRemaining, int highlight)\r
8738 {\r
8739   HDC hdc;\r
8740   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8741 \r
8742   if(appData.noGUI) return;\r
8743   hdc = GetDC(hwndMain);\r
8744   if (!IsIconic(hwndMain)) {\r
8745     DisplayAClock(hdc, timeRemaining, highlight, \r
8746                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8747   }\r
8748   if (highlight && iconCurrent == iconWhite) {\r
8749     iconCurrent = iconBlack;\r
8750     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8751     if (IsIconic(hwndMain)) {\r
8752       DrawIcon(hdc, 2, 2, iconCurrent);\r
8753     }\r
8754   }\r
8755   (void) ReleaseDC(hwndMain, hdc);\r
8756   if (hwndConsole)\r
8757     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8758 }\r
8759 \r
8760 \r
8761 int\r
8762 LoadGameTimerRunning()\r
8763 {\r
8764   return loadGameTimerEvent != 0;\r
8765 }\r
8766 \r
8767 int\r
8768 StopLoadGameTimer()\r
8769 {\r
8770   if (loadGameTimerEvent == 0) return FALSE;\r
8771   KillTimer(hwndMain, loadGameTimerEvent);\r
8772   loadGameTimerEvent = 0;\r
8773   return TRUE;\r
8774 }\r
8775 \r
8776 void\r
8777 StartLoadGameTimer(long millisec)\r
8778 {\r
8779   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8780                                 (UINT) millisec, NULL);\r
8781 }\r
8782 \r
8783 void\r
8784 AutoSaveGame()\r
8785 {\r
8786   char *defName;\r
8787   FILE *f;\r
8788   char fileTitle[MSG_SIZ];\r
8789 \r
8790   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8791   f = OpenFileDialog(hwndMain, "a", defName,\r
8792                      appData.oldSaveStyle ? "gam" : "pgn",\r
8793                      GAME_FILT, \r
8794                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8795   if (f != NULL) {\r
8796     SaveGame(f, 0, "");\r
8797     fclose(f);\r
8798   }\r
8799 }\r
8800 \r
8801 \r
8802 void\r
8803 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8804 {\r
8805   if (delayedTimerEvent != 0) {\r
8806     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8807       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8808     }\r
8809     KillTimer(hwndMain, delayedTimerEvent);\r
8810     delayedTimerEvent = 0;\r
8811     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8812     delayedTimerCallback();\r
8813   }\r
8814   delayedTimerCallback = cb;\r
8815   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8816                                 (UINT) millisec, NULL);\r
8817 }\r
8818 \r
8819 DelayedEventCallback\r
8820 GetDelayedEvent()\r
8821 {\r
8822   if (delayedTimerEvent) {\r
8823     return delayedTimerCallback;\r
8824   } else {\r
8825     return NULL;\r
8826   }\r
8827 }\r
8828 \r
8829 void\r
8830 CancelDelayedEvent()\r
8831 {\r
8832   if (delayedTimerEvent) {\r
8833     KillTimer(hwndMain, delayedTimerEvent);\r
8834     delayedTimerEvent = 0;\r
8835   }\r
8836 }\r
8837 \r
8838 DWORD GetWin32Priority(int nice)\r
8839 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8840 /*\r
8841 REALTIME_PRIORITY_CLASS     0x00000100\r
8842 HIGH_PRIORITY_CLASS         0x00000080\r
8843 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8844 NORMAL_PRIORITY_CLASS       0x00000020\r
8845 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8846 IDLE_PRIORITY_CLASS         0x00000040\r
8847 */\r
8848         if (nice < -15) return 0x00000080;\r
8849         if (nice < 0)   return 0x00008000;\r
8850         if (nice == 0)  return 0x00000020;\r
8851         if (nice < 15)  return 0x00004000;\r
8852         return 0x00000040;\r
8853 }\r
8854 \r
8855 /* Start a child process running the given program.\r
8856    The process's standard output can be read from "from", and its\r
8857    standard input can be written to "to".\r
8858    Exit with fatal error if anything goes wrong.\r
8859    Returns an opaque pointer that can be used to destroy the process\r
8860    later.\r
8861 */\r
8862 int\r
8863 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8864 {\r
8865 #define BUFSIZE 4096\r
8866 \r
8867   HANDLE hChildStdinRd, hChildStdinWr,\r
8868     hChildStdoutRd, hChildStdoutWr;\r
8869   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8870   SECURITY_ATTRIBUTES saAttr;\r
8871   BOOL fSuccess;\r
8872   PROCESS_INFORMATION piProcInfo;\r
8873   STARTUPINFO siStartInfo;\r
8874   ChildProc *cp;\r
8875   char buf[MSG_SIZ];\r
8876   DWORD err;\r
8877 \r
8878   if (appData.debugMode) {\r
8879     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8880   }\r
8881 \r
8882   *pr = NoProc;\r
8883 \r
8884   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8885   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8886   saAttr.bInheritHandle = TRUE;\r
8887   saAttr.lpSecurityDescriptor = NULL;\r
8888 \r
8889   /*\r
8890    * The steps for redirecting child's STDOUT:\r
8891    *     1. Create anonymous pipe to be STDOUT for child.\r
8892    *     2. Create a noninheritable duplicate of read handle,\r
8893    *         and close the inheritable read handle.\r
8894    */\r
8895 \r
8896   /* Create a pipe for the child's STDOUT. */\r
8897   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8898     return GetLastError();\r
8899   }\r
8900 \r
8901   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8902   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8903                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8904                              FALSE,     /* not inherited */\r
8905                              DUPLICATE_SAME_ACCESS);\r
8906   if (! fSuccess) {\r
8907     return GetLastError();\r
8908   }\r
8909   CloseHandle(hChildStdoutRd);\r
8910 \r
8911   /*\r
8912    * The steps for redirecting child's STDIN:\r
8913    *     1. Create anonymous pipe to be STDIN for child.\r
8914    *     2. Create a noninheritable duplicate of write handle,\r
8915    *         and close the inheritable write handle.\r
8916    */\r
8917 \r
8918   /* Create a pipe for the child's STDIN. */\r
8919   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8920     return GetLastError();\r
8921   }\r
8922 \r
8923   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8924   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8925                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8926                              FALSE,     /* not inherited */\r
8927                              DUPLICATE_SAME_ACCESS);\r
8928   if (! fSuccess) {\r
8929     return GetLastError();\r
8930   }\r
8931   CloseHandle(hChildStdinWr);\r
8932 \r
8933   /* Arrange to (1) look in dir for the child .exe file, and\r
8934    * (2) have dir be the child's working directory.  Interpret\r
8935    * dir relative to the directory WinBoard loaded from. */\r
8936   GetCurrentDirectory(MSG_SIZ, buf);\r
8937   SetCurrentDirectory(installDir);\r
8938   SetCurrentDirectory(dir);\r
8939 \r
8940   /* Now create the child process. */\r
8941 \r
8942   siStartInfo.cb = sizeof(STARTUPINFO);\r
8943   siStartInfo.lpReserved = NULL;\r
8944   siStartInfo.lpDesktop = NULL;\r
8945   siStartInfo.lpTitle = NULL;\r
8946   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8947   siStartInfo.cbReserved2 = 0;\r
8948   siStartInfo.lpReserved2 = NULL;\r
8949   siStartInfo.hStdInput = hChildStdinRd;\r
8950   siStartInfo.hStdOutput = hChildStdoutWr;\r
8951   siStartInfo.hStdError = hChildStdoutWr;\r
8952 \r
8953   fSuccess = CreateProcess(NULL,\r
8954                            cmdLine,        /* command line */\r
8955                            NULL,           /* process security attributes */\r
8956                            NULL,           /* primary thread security attrs */\r
8957                            TRUE,           /* handles are inherited */\r
8958                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8959                            NULL,           /* use parent's environment */\r
8960                            NULL,\r
8961                            &siStartInfo, /* STARTUPINFO pointer */\r
8962                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8963 \r
8964   err = GetLastError();\r
8965   SetCurrentDirectory(buf); /* return to prev directory */\r
8966   if (! fSuccess) {\r
8967     return err;\r
8968   }\r
8969 \r
8970   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8971     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8972     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8973   }\r
8974 \r
8975   /* Close the handles we don't need in the parent */\r
8976   CloseHandle(piProcInfo.hThread);\r
8977   CloseHandle(hChildStdinRd);\r
8978   CloseHandle(hChildStdoutWr);\r
8979 \r
8980   /* Prepare return value */\r
8981   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8982   cp->kind = CPReal;\r
8983   cp->hProcess = piProcInfo.hProcess;\r
8984   cp->pid = piProcInfo.dwProcessId;\r
8985   cp->hFrom = hChildStdoutRdDup;\r
8986   cp->hTo = hChildStdinWrDup;\r
8987 \r
8988   *pr = (void *) cp;\r
8989 \r
8990   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8991      2000 where engines sometimes don't see the initial command(s)\r
8992      from WinBoard and hang.  I don't understand how that can happen,\r
8993      but the Sleep is harmless, so I've put it in.  Others have also\r
8994      reported what may be the same problem, so hopefully this will fix\r
8995      it for them too.  */\r
8996   Sleep(500);\r
8997 \r
8998   return NO_ERROR;\r
8999 }\r
9000 \r
9001 \r
9002 void\r
9003 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9004 {\r
9005   ChildProc *cp; int result;\r
9006 \r
9007   cp = (ChildProc *) pr;\r
9008   if (cp == NULL) return;\r
9009 \r
9010   switch (cp->kind) {\r
9011   case CPReal:\r
9012     /* TerminateProcess is considered harmful, so... */\r
9013     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9014     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9015     /* The following doesn't work because the chess program\r
9016        doesn't "have the same console" as WinBoard.  Maybe\r
9017        we could arrange for this even though neither WinBoard\r
9018        nor the chess program uses a console for stdio? */\r
9019     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9020 \r
9021     /* [AS] Special termination modes for misbehaving programs... */\r
9022     if( signal == 9 ) { \r
9023         result = TerminateProcess( cp->hProcess, 0 );\r
9024 \r
9025         if ( appData.debugMode) {\r
9026             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9027         }\r
9028     }\r
9029     else if( signal == 10 ) {\r
9030         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9031 \r
9032         if( dw != WAIT_OBJECT_0 ) {\r
9033             result = TerminateProcess( cp->hProcess, 0 );\r
9034 \r
9035             if ( appData.debugMode) {\r
9036                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9037             }\r
9038 \r
9039         }\r
9040     }\r
9041 \r
9042     CloseHandle(cp->hProcess);\r
9043     break;\r
9044 \r
9045   case CPComm:\r
9046     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9047     break;\r
9048 \r
9049   case CPSock:\r
9050     closesocket(cp->sock);\r
9051     WSACleanup();\r
9052     break;\r
9053 \r
9054   case CPRcmd:\r
9055     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9056     closesocket(cp->sock);\r
9057     closesocket(cp->sock2);\r
9058     WSACleanup();\r
9059     break;\r
9060   }\r
9061   free(cp);\r
9062 }\r
9063 \r
9064 void\r
9065 InterruptChildProcess(ProcRef pr)\r
9066 {\r
9067   ChildProc *cp;\r
9068 \r
9069   cp = (ChildProc *) pr;\r
9070   if (cp == NULL) return;\r
9071   switch (cp->kind) {\r
9072   case CPReal:\r
9073     /* The following doesn't work because the chess program\r
9074        doesn't "have the same console" as WinBoard.  Maybe\r
9075        we could arrange for this even though neither WinBoard\r
9076        nor the chess program uses a console for stdio */\r
9077     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9078     break;\r
9079 \r
9080   case CPComm:\r
9081   case CPSock:\r
9082     /* Can't interrupt */\r
9083     break;\r
9084 \r
9085   case CPRcmd:\r
9086     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9087     break;\r
9088   }\r
9089 }\r
9090 \r
9091 \r
9092 int\r
9093 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9094 {\r
9095   char cmdLine[MSG_SIZ];\r
9096 \r
9097   if (port[0] == NULLCHAR) {\r
9098     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9099   } else {\r
9100     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9101   }\r
9102   return StartChildProcess(cmdLine, "", pr);\r
9103 }\r
9104 \r
9105 \r
9106 /* Code to open TCP sockets */\r
9107 \r
9108 int\r
9109 OpenTCP(char *host, char *port, ProcRef *pr)\r
9110 {\r
9111   ChildProc *cp;\r
9112   int err;\r
9113   SOCKET s;\r
9114   struct sockaddr_in sa, mysa;\r
9115   struct hostent FAR *hp;\r
9116   unsigned short uport;\r
9117   WORD wVersionRequested;\r
9118   WSADATA wsaData;\r
9119 \r
9120   /* Initialize socket DLL */\r
9121   wVersionRequested = MAKEWORD(1, 1);\r
9122   err = WSAStartup(wVersionRequested, &wsaData);\r
9123   if (err != 0) return err;\r
9124 \r
9125   /* Make socket */\r
9126   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9127     err = WSAGetLastError();\r
9128     WSACleanup();\r
9129     return err;\r
9130   }\r
9131 \r
9132   /* Bind local address using (mostly) don't-care values.\r
9133    */\r
9134   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9135   mysa.sin_family = AF_INET;\r
9136   mysa.sin_addr.s_addr = INADDR_ANY;\r
9137   uport = (unsigned short) 0;\r
9138   mysa.sin_port = htons(uport);\r
9139   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9140       == SOCKET_ERROR) {\r
9141     err = WSAGetLastError();\r
9142     WSACleanup();\r
9143     return err;\r
9144   }\r
9145 \r
9146   /* Resolve remote host name */\r
9147   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9148   if (!(hp = gethostbyname(host))) {\r
9149     unsigned int b0, b1, b2, b3;\r
9150 \r
9151     err = WSAGetLastError();\r
9152 \r
9153     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9154       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9155       hp->h_addrtype = AF_INET;\r
9156       hp->h_length = 4;\r
9157       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9158       hp->h_addr_list[0] = (char *) malloc(4);\r
9159       hp->h_addr_list[0][0] = (char) b0;\r
9160       hp->h_addr_list[0][1] = (char) b1;\r
9161       hp->h_addr_list[0][2] = (char) b2;\r
9162       hp->h_addr_list[0][3] = (char) b3;\r
9163     } else {\r
9164       WSACleanup();\r
9165       return err;\r
9166     }\r
9167   }\r
9168   sa.sin_family = hp->h_addrtype;\r
9169   uport = (unsigned short) atoi(port);\r
9170   sa.sin_port = htons(uport);\r
9171   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9172 \r
9173   /* Make connection */\r
9174   if (connect(s, (struct sockaddr *) &sa,\r
9175               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9176     err = WSAGetLastError();\r
9177     WSACleanup();\r
9178     return err;\r
9179   }\r
9180 \r
9181   /* Prepare return value */\r
9182   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9183   cp->kind = CPSock;\r
9184   cp->sock = s;\r
9185   *pr = (ProcRef *) cp;\r
9186 \r
9187   return NO_ERROR;\r
9188 }\r
9189 \r
9190 int\r
9191 OpenCommPort(char *name, ProcRef *pr)\r
9192 {\r
9193   HANDLE h;\r
9194   COMMTIMEOUTS ct;\r
9195   ChildProc *cp;\r
9196   char fullname[MSG_SIZ];\r
9197 \r
9198   if (*name != '\\')\r
9199     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9200   else\r
9201     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9202 \r
9203   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9204                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9205   if (h == (HANDLE) -1) {\r
9206     return GetLastError();\r
9207   }\r
9208   hCommPort = h;\r
9209 \r
9210   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9211 \r
9212   /* Accumulate characters until a 100ms pause, then parse */\r
9213   ct.ReadIntervalTimeout = 100;\r
9214   ct.ReadTotalTimeoutMultiplier = 0;\r
9215   ct.ReadTotalTimeoutConstant = 0;\r
9216   ct.WriteTotalTimeoutMultiplier = 0;\r
9217   ct.WriteTotalTimeoutConstant = 0;\r
9218   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9219 \r
9220   /* Prepare return value */\r
9221   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9222   cp->kind = CPComm;\r
9223   cp->hFrom = h;\r
9224   cp->hTo = h;\r
9225   *pr = (ProcRef *) cp;\r
9226 \r
9227   return NO_ERROR;\r
9228 }\r
9229 \r
9230 int\r
9231 OpenLoopback(ProcRef *pr)\r
9232 {\r
9233   DisplayFatalError(_("Not implemented"), 0, 1);\r
9234   return NO_ERROR;\r
9235 }\r
9236 \r
9237 \r
9238 int\r
9239 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9240 {\r
9241   ChildProc *cp;\r
9242   int err;\r
9243   SOCKET s, s2, s3;\r
9244   struct sockaddr_in sa, mysa;\r
9245   struct hostent FAR *hp;\r
9246   unsigned short uport;\r
9247   WORD wVersionRequested;\r
9248   WSADATA wsaData;\r
9249   int fromPort;\r
9250   char stderrPortStr[MSG_SIZ];\r
9251 \r
9252   /* Initialize socket DLL */\r
9253   wVersionRequested = MAKEWORD(1, 1);\r
9254   err = WSAStartup(wVersionRequested, &wsaData);\r
9255   if (err != 0) return err;\r
9256 \r
9257   /* Resolve remote host name */\r
9258   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9259   if (!(hp = gethostbyname(host))) {\r
9260     unsigned int b0, b1, b2, b3;\r
9261 \r
9262     err = WSAGetLastError();\r
9263 \r
9264     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9265       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9266       hp->h_addrtype = AF_INET;\r
9267       hp->h_length = 4;\r
9268       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9269       hp->h_addr_list[0] = (char *) malloc(4);\r
9270       hp->h_addr_list[0][0] = (char) b0;\r
9271       hp->h_addr_list[0][1] = (char) b1;\r
9272       hp->h_addr_list[0][2] = (char) b2;\r
9273       hp->h_addr_list[0][3] = (char) b3;\r
9274     } else {\r
9275       WSACleanup();\r
9276       return err;\r
9277     }\r
9278   }\r
9279   sa.sin_family = hp->h_addrtype;\r
9280   uport = (unsigned short) 514;\r
9281   sa.sin_port = htons(uport);\r
9282   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9283 \r
9284   /* Bind local socket to unused "privileged" port address\r
9285    */\r
9286   s = INVALID_SOCKET;\r
9287   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9288   mysa.sin_family = AF_INET;\r
9289   mysa.sin_addr.s_addr = INADDR_ANY;\r
9290   for (fromPort = 1023;; fromPort--) {\r
9291     if (fromPort < 0) {\r
9292       WSACleanup();\r
9293       return WSAEADDRINUSE;\r
9294     }\r
9295     if (s == INVALID_SOCKET) {\r
9296       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9297         err = WSAGetLastError();\r
9298         WSACleanup();\r
9299         return err;\r
9300       }\r
9301     }\r
9302     uport = (unsigned short) fromPort;\r
9303     mysa.sin_port = htons(uport);\r
9304     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9305         == SOCKET_ERROR) {\r
9306       err = WSAGetLastError();\r
9307       if (err == WSAEADDRINUSE) continue;\r
9308       WSACleanup();\r
9309       return err;\r
9310     }\r
9311     if (connect(s, (struct sockaddr *) &sa,\r
9312       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9313       err = WSAGetLastError();\r
9314       if (err == WSAEADDRINUSE) {\r
9315         closesocket(s);\r
9316         s = -1;\r
9317         continue;\r
9318       }\r
9319       WSACleanup();\r
9320       return err;\r
9321     }\r
9322     break;\r
9323   }\r
9324 \r
9325   /* Bind stderr local socket to unused "privileged" port address\r
9326    */\r
9327   s2 = INVALID_SOCKET;\r
9328   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9329   mysa.sin_family = AF_INET;\r
9330   mysa.sin_addr.s_addr = INADDR_ANY;\r
9331   for (fromPort = 1023;; fromPort--) {\r
9332     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9333     if (fromPort < 0) {\r
9334       (void) closesocket(s);\r
9335       WSACleanup();\r
9336       return WSAEADDRINUSE;\r
9337     }\r
9338     if (s2 == INVALID_SOCKET) {\r
9339       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9340         err = WSAGetLastError();\r
9341         closesocket(s);\r
9342         WSACleanup();\r
9343         return err;\r
9344       }\r
9345     }\r
9346     uport = (unsigned short) fromPort;\r
9347     mysa.sin_port = htons(uport);\r
9348     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9349         == SOCKET_ERROR) {\r
9350       err = WSAGetLastError();\r
9351       if (err == WSAEADDRINUSE) continue;\r
9352       (void) closesocket(s);\r
9353       WSACleanup();\r
9354       return err;\r
9355     }\r
9356     if (listen(s2, 1) == SOCKET_ERROR) {\r
9357       err = WSAGetLastError();\r
9358       if (err == WSAEADDRINUSE) {\r
9359         closesocket(s2);\r
9360         s2 = INVALID_SOCKET;\r
9361         continue;\r
9362       }\r
9363       (void) closesocket(s);\r
9364       (void) closesocket(s2);\r
9365       WSACleanup();\r
9366       return err;\r
9367     }\r
9368     break;\r
9369   }\r
9370   prevStderrPort = fromPort; // remember port used\r
9371   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9372 \r
9373   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9374     err = WSAGetLastError();\r
9375     (void) closesocket(s);\r
9376     (void) closesocket(s2);\r
9377     WSACleanup();\r
9378     return err;\r
9379   }\r
9380 \r
9381   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9382     err = WSAGetLastError();\r
9383     (void) closesocket(s);\r
9384     (void) closesocket(s2);\r
9385     WSACleanup();\r
9386     return err;\r
9387   }\r
9388   if (*user == NULLCHAR) user = UserName();\r
9389   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9390     err = WSAGetLastError();\r
9391     (void) closesocket(s);\r
9392     (void) closesocket(s2);\r
9393     WSACleanup();\r
9394     return err;\r
9395   }\r
9396   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9397     err = WSAGetLastError();\r
9398     (void) closesocket(s);\r
9399     (void) closesocket(s2);\r
9400     WSACleanup();\r
9401     return err;\r
9402   }\r
9403 \r
9404   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9405     err = WSAGetLastError();\r
9406     (void) closesocket(s);\r
9407     (void) closesocket(s2);\r
9408     WSACleanup();\r
9409     return err;\r
9410   }\r
9411   (void) closesocket(s2);  /* Stop listening */\r
9412 \r
9413   /* Prepare return value */\r
9414   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9415   cp->kind = CPRcmd;\r
9416   cp->sock = s;\r
9417   cp->sock2 = s3;\r
9418   *pr = (ProcRef *) cp;\r
9419 \r
9420   return NO_ERROR;\r
9421 }\r
9422 \r
9423 \r
9424 InputSourceRef\r
9425 AddInputSource(ProcRef pr, int lineByLine,\r
9426                InputCallback func, VOIDSTAR closure)\r
9427 {\r
9428   InputSource *is, *is2 = NULL;\r
9429   ChildProc *cp = (ChildProc *) pr;\r
9430 \r
9431   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9432   is->lineByLine = lineByLine;\r
9433   is->func = func;\r
9434   is->closure = closure;\r
9435   is->second = NULL;\r
9436   is->next = is->buf;\r
9437   if (pr == NoProc) {\r
9438     is->kind = CPReal;\r
9439     consoleInputSource = is;\r
9440   } else {\r
9441     is->kind = cp->kind;\r
9442     /* \r
9443         [AS] Try to avoid a race condition if the thread is given control too early:\r
9444         we create all threads suspended so that the is->hThread variable can be\r
9445         safely assigned, then let the threads start with ResumeThread.\r
9446     */\r
9447     switch (cp->kind) {\r
9448     case CPReal:\r
9449       is->hFile = cp->hFrom;\r
9450       cp->hFrom = NULL; /* now owned by InputThread */\r
9451       is->hThread =\r
9452         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9453                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9454       break;\r
9455 \r
9456     case CPComm:\r
9457       is->hFile = cp->hFrom;\r
9458       cp->hFrom = NULL; /* now owned by InputThread */\r
9459       is->hThread =\r
9460         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9461                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9462       break;\r
9463 \r
9464     case CPSock:\r
9465       is->sock = cp->sock;\r
9466       is->hThread =\r
9467         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9468                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9469       break;\r
9470 \r
9471     case CPRcmd:\r
9472       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9473       *is2 = *is;\r
9474       is->sock = cp->sock;\r
9475       is->second = is2;\r
9476       is2->sock = cp->sock2;\r
9477       is2->second = is2;\r
9478       is->hThread =\r
9479         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9480                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9481       is2->hThread =\r
9482         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9483                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9484       break;\r
9485     }\r
9486 \r
9487     if( is->hThread != NULL ) {\r
9488         ResumeThread( is->hThread );\r
9489     }\r
9490 \r
9491     if( is2 != NULL && is2->hThread != NULL ) {\r
9492         ResumeThread( is2->hThread );\r
9493     }\r
9494   }\r
9495 \r
9496   return (InputSourceRef) is;\r
9497 }\r
9498 \r
9499 void\r
9500 RemoveInputSource(InputSourceRef isr)\r
9501 {\r
9502   InputSource *is;\r
9503 \r
9504   is = (InputSource *) isr;\r
9505   is->hThread = NULL;  /* tell thread to stop */\r
9506   CloseHandle(is->hThread);\r
9507   if (is->second != NULL) {\r
9508     is->second->hThread = NULL;\r
9509     CloseHandle(is->second->hThread);\r
9510   }\r
9511 }\r
9512 \r
9513 int no_wrap(char *message, int count)\r
9514 {\r
9515     ConsoleOutput(message, count, FALSE);\r
9516     return count;\r
9517 }\r
9518 \r
9519 int\r
9520 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9521 {\r
9522   DWORD dOutCount;\r
9523   int outCount = SOCKET_ERROR;\r
9524   ChildProc *cp = (ChildProc *) pr;\r
9525   static OVERLAPPED ovl;\r
9526   static int line = 0;\r
9527 \r
9528   if (pr == NoProc)\r
9529   {\r
9530     if (appData.noJoin || !appData.useInternalWrap)\r
9531       return no_wrap(message, count);\r
9532     else\r
9533     {\r
9534       int width = get_term_width();\r
9535       int len = wrap(NULL, message, count, width, &line);\r
9536       char *msg = malloc(len);\r
9537       int dbgchk;\r
9538 \r
9539       if (!msg)\r
9540         return no_wrap(message, count);\r
9541       else\r
9542       {\r
9543         dbgchk = wrap(msg, message, count, width, &line);\r
9544         if (dbgchk != len && appData.debugMode)\r
9545             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9546         ConsoleOutput(msg, len, FALSE);\r
9547         free(msg);\r
9548         return len;\r
9549       }\r
9550     }\r
9551   }\r
9552 \r
9553   if (ovl.hEvent == NULL) {\r
9554     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9555   }\r
9556   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9557 \r
9558   switch (cp->kind) {\r
9559   case CPSock:\r
9560   case CPRcmd:\r
9561     outCount = send(cp->sock, message, count, 0);\r
9562     if (outCount == SOCKET_ERROR) {\r
9563       *outError = WSAGetLastError();\r
9564     } else {\r
9565       *outError = NO_ERROR;\r
9566     }\r
9567     break;\r
9568 \r
9569   case CPReal:\r
9570     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9571                   &dOutCount, NULL)) {\r
9572       *outError = NO_ERROR;\r
9573       outCount = (int) dOutCount;\r
9574     } else {\r
9575       *outError = GetLastError();\r
9576     }\r
9577     break;\r
9578 \r
9579   case CPComm:\r
9580     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9581                             &dOutCount, &ovl);\r
9582     if (*outError == NO_ERROR) {\r
9583       outCount = (int) dOutCount;\r
9584     }\r
9585     break;\r
9586   }\r
9587   return outCount;\r
9588 }\r
9589 \r
9590 int\r
9591 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9592                        long msdelay)\r
9593 {\r
9594   /* Ignore delay, not implemented for WinBoard */\r
9595   return OutputToProcess(pr, message, count, outError);\r
9596 }\r
9597 \r
9598 \r
9599 void\r
9600 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9601                         char *buf, int count, int error)\r
9602 {\r
9603   DisplayFatalError(_("Not implemented"), 0, 1);\r
9604 }\r
9605 \r
9606 /* see wgamelist.c for Game List functions */\r
9607 /* see wedittags.c for Edit Tags functions */\r
9608 \r
9609 \r
9610 VOID\r
9611 ICSInitScript()\r
9612 {\r
9613   FILE *f;\r
9614   char buf[MSG_SIZ];\r
9615   char *dummy;\r
9616 \r
9617   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9618     f = fopen(buf, "r");\r
9619     if (f != NULL) {\r
9620       ProcessICSInitScript(f);\r
9621       fclose(f);\r
9622     }\r
9623   }\r
9624 }\r
9625 \r
9626 \r
9627 VOID\r
9628 StartAnalysisClock()\r
9629 {\r
9630   if (analysisTimerEvent) return;\r
9631   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9632                                         (UINT) 2000, NULL);\r
9633 }\r
9634 \r
9635 VOID\r
9636 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9637 {\r
9638   highlightInfo.sq[0].x = fromX;\r
9639   highlightInfo.sq[0].y = fromY;\r
9640   highlightInfo.sq[1].x = toX;\r
9641   highlightInfo.sq[1].y = toY;\r
9642 }\r
9643 \r
9644 VOID\r
9645 ClearHighlights()\r
9646 {\r
9647   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9648     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9649 }\r
9650 \r
9651 VOID\r
9652 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9653 {\r
9654   premoveHighlightInfo.sq[0].x = fromX;\r
9655   premoveHighlightInfo.sq[0].y = fromY;\r
9656   premoveHighlightInfo.sq[1].x = toX;\r
9657   premoveHighlightInfo.sq[1].y = toY;\r
9658 }\r
9659 \r
9660 VOID\r
9661 ClearPremoveHighlights()\r
9662 {\r
9663   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9664     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9665 }\r
9666 \r
9667 VOID\r
9668 ShutDownFrontEnd()\r
9669 {\r
9670   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9671   DeleteClipboardTempFiles();\r
9672 }\r
9673 \r
9674 void\r
9675 BoardToTop()\r
9676 {\r
9677     if (IsIconic(hwndMain))\r
9678       ShowWindow(hwndMain, SW_RESTORE);\r
9679 \r
9680     SetActiveWindow(hwndMain);\r
9681 }\r
9682 \r
9683 /*\r
9684  * Prototypes for animation support routines\r
9685  */\r
9686 static void ScreenSquare(int column, int row, POINT * pt);\r
9687 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9688      POINT frames[], int * nFrames);\r
9689 \r
9690 \r
9691 #define kFactor 4\r
9692 \r
9693 void\r
9694 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9695 {       // [HGM] atomic: animate blast wave\r
9696         int i;\r
9697 \r
9698         explodeInfo.fromX = fromX;\r
9699         explodeInfo.fromY = fromY;\r
9700         explodeInfo.toX = toX;\r
9701         explodeInfo.toY = toY;\r
9702         for(i=1; i<4*kFactor; i++) {\r
9703             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9704             DrawPosition(FALSE, board);\r
9705             Sleep(appData.animSpeed);\r
9706         }\r
9707         explodeInfo.radius = 0;\r
9708         DrawPosition(TRUE, board);\r
9709 }\r
9710 \r
9711 void\r
9712 AnimateMove(board, fromX, fromY, toX, toY)\r
9713      Board board;\r
9714      int fromX;\r
9715      int fromY;\r
9716      int toX;\r
9717      int toY;\r
9718 {\r
9719   ChessSquare piece;\r
9720   POINT start, finish, mid;\r
9721   POINT frames[kFactor * 2 + 1];\r
9722   int nFrames, n;\r
9723 \r
9724   if (!appData.animate) return;\r
9725   if (doingSizing) return;\r
9726   if (fromY < 0 || fromX < 0) return;\r
9727   piece = board[fromY][fromX];\r
9728   if (piece >= EmptySquare) return;\r
9729 \r
9730   ScreenSquare(fromX, fromY, &start);\r
9731   ScreenSquare(toX, toY, &finish);\r
9732 \r
9733   /* All moves except knight jumps move in straight line */\r
9734   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9735     mid.x = start.x + (finish.x - start.x) / 2;\r
9736     mid.y = start.y + (finish.y - start.y) / 2;\r
9737   } else {\r
9738     /* Knight: make straight movement then diagonal */\r
9739     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9740        mid.x = start.x + (finish.x - start.x) / 2;\r
9741        mid.y = start.y;\r
9742      } else {\r
9743        mid.x = start.x;\r
9744        mid.y = start.y + (finish.y - start.y) / 2;\r
9745      }\r
9746   }\r
9747   \r
9748   /* Don't use as many frames for very short moves */\r
9749   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9750     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9751   else\r
9752     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9753 \r
9754   animInfo.from.x = fromX;\r
9755   animInfo.from.y = fromY;\r
9756   animInfo.to.x = toX;\r
9757   animInfo.to.y = toY;\r
9758   animInfo.lastpos = start;\r
9759   animInfo.piece = piece;\r
9760   for (n = 0; n < nFrames; n++) {\r
9761     animInfo.pos = frames[n];\r
9762     DrawPosition(FALSE, NULL);\r
9763     animInfo.lastpos = animInfo.pos;\r
9764     Sleep(appData.animSpeed);\r
9765   }\r
9766   animInfo.pos = finish;\r
9767   DrawPosition(FALSE, NULL);\r
9768   animInfo.piece = EmptySquare;\r
9769   Explode(board, fromX, fromY, toX, toY);\r
9770 }\r
9771 \r
9772 /*      Convert board position to corner of screen rect and color       */\r
9773 \r
9774 static void\r
9775 ScreenSquare(column, row, pt)\r
9776      int column; int row; POINT * pt;\r
9777 {\r
9778   if (flipView) {\r
9779     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9780     pt->y = lineGap + row * (squareSize + lineGap);\r
9781   } else {\r
9782     pt->x = lineGap + column * (squareSize + lineGap);\r
9783     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9784   }\r
9785 }\r
9786 \r
9787 /*      Generate a series of frame coords from start->mid->finish.\r
9788         The movement rate doubles until the half way point is\r
9789         reached, then halves back down to the final destination,\r
9790         which gives a nice slow in/out effect. The algorithmn\r
9791         may seem to generate too many intermediates for short\r
9792         moves, but remember that the purpose is to attract the\r
9793         viewers attention to the piece about to be moved and\r
9794         then to where it ends up. Too few frames would be less\r
9795         noticeable.                                             */\r
9796 \r
9797 static void\r
9798 Tween(start, mid, finish, factor, frames, nFrames)\r
9799      POINT * start; POINT * mid;\r
9800      POINT * finish; int factor;\r
9801      POINT frames[]; int * nFrames;\r
9802 {\r
9803   int n, fraction = 1, count = 0;\r
9804 \r
9805   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9806   for (n = 0; n < factor; n++)\r
9807     fraction *= 2;\r
9808   for (n = 0; n < factor; n++) {\r
9809     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9810     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9811     count ++;\r
9812     fraction = fraction / 2;\r
9813   }\r
9814   \r
9815   /* Midpoint */\r
9816   frames[count] = *mid;\r
9817   count ++;\r
9818   \r
9819   /* Slow out, stepping 1/2, then 1/4, ... */\r
9820   fraction = 2;\r
9821   for (n = 0; n < factor; n++) {\r
9822     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9823     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9824     count ++;\r
9825     fraction = fraction * 2;\r
9826   }\r
9827   *nFrames = count;\r
9828 }\r
9829 \r
9830 void\r
9831 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9832 {\r
9833     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9834 \r
9835     EvalGraphSet( first, last, current, pvInfoList );\r
9836 \r
9837     MakeEngineOutputTitle();\r
9838 }\r
9839 \r
9840 void\r
9841 SettingsPopUp(ChessProgramState *cps)\r
9842 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9843       EngineOptionsPopup(savedHwnd, cps);\r
9844 }\r
9845 \r
9846 int flock(int fid, int code)\r
9847 {\r
9848     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9849     OVERLAPPED ov;\r
9850     ov.hEvent = NULL;\r
9851     ov.Offset = 0;\r
9852     ov.OffsetHigh = 0;\r
9853     switch(code) {\r
9854       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9855       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9856       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9857       default: return -1;\r
9858     }\r
9859     return 0;\r
9860 }\r