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