Implement book-creation functions
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
557   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
558   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
559   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
560   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
561   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         char *opponent = "";\r
1010         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1011         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1012         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1013         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1014             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1015             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1016         }\r
1017       } else\r
1018       if(appData.directory[n] && appData.directory[n][0]) {\r
1019         SetCurrentDirectory(appData.directory[n]);\r
1020         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1021       }\r
1022   }\r
1023   SetCurrentDirectory(dir); /* return to prev directory */\r
1024 }\r
1025 \r
1026 VOID\r
1027 InitTextures()\r
1028 {\r
1029   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1030   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1031   \r
1032   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1033       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1034       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035       liteBackTextureMode = appData.liteBackTextureMode;\r
1036 \r
1037       if (liteBackTexture == NULL && appData.debugMode) {\r
1038           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1039       }\r
1040   }\r
1041   \r
1042   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1043       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1044       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045       darkBackTextureMode = appData.darkBackTextureMode;\r
1046 \r
1047       if (darkBackTexture == NULL && appData.debugMode) {\r
1048           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1049       }\r
1050   }\r
1051 }\r
1052 \r
1053 BOOL\r
1054 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1055 {\r
1056   HWND hwnd; /* Main window handle. */\r
1057   int ibs;\r
1058   WINDOWPLACEMENT wp;\r
1059   char *filepart;\r
1060 \r
1061   hInst = hInstance;    /* Store instance handle in our global variable */\r
1062   programName = szAppName;\r
1063 \r
1064   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1065     *filepart = NULLCHAR;\r
1066   } else {\r
1067     GetCurrentDirectory(MSG_SIZ, installDir);\r
1068   }\r
1069   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1070   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1071   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1072   /* xboard, and older WinBoards, controlled the move sound with the\r
1073      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1074      always turn the option on (so that the backend will call us),\r
1075      then let the user turn the sound off by setting it to silence if\r
1076      desired.  To accommodate old winboard.ini files saved by old\r
1077      versions of WinBoard, we also turn off the sound if the option\r
1078      was initially set to false. [HGM] taken out of InitAppData */\r
1079   if (!appData.ringBellAfterMoves) {\r
1080     sounds[(int)SoundMove].name = strdup("");\r
1081     appData.ringBellAfterMoves = TRUE;\r
1082   }\r
1083   if (appData.debugMode) {\r
1084     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1085     setbuf(debugFP, NULL);\r
1086   }\r
1087 \r
1088   LoadLanguageFile(appData.language);\r
1089 \r
1090   InitBackEnd1();\r
1091 \r
1092 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1093 //  InitEngineUCI( installDir, &second );\r
1094 \r
1095   /* Create a main window for this application instance. */\r
1096   hwnd = CreateWindow(szAppName, szTitle,\r
1097                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1098                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1099                       NULL, NULL, hInstance, NULL);\r
1100   hwndMain = hwnd;\r
1101 \r
1102   /* If window could not be created, return "failure" */\r
1103   if (!hwnd) {\r
1104     return (FALSE);\r
1105   }\r
1106 \r
1107   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1108   LoadLogo(&first, 0, FALSE);\r
1109   LoadLogo(&second, 1, appData.icsActive);\r
1110 \r
1111   SetUserLogo();\r
1112 \r
1113   iconWhite = LoadIcon(hInstance, "icon_white");\r
1114   iconBlack = LoadIcon(hInstance, "icon_black");\r
1115   iconCurrent = iconWhite;\r
1116   InitDrawingColors();\r
1117   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1118   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1119   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1120     /* Compute window size for each board size, and use the largest\r
1121        size that fits on this screen as the default. */\r
1122     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1123     if (boardSize == (BoardSize)-1 &&\r
1124         winH <= screenHeight\r
1125            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1126         && winW <= screenWidth) {\r
1127       boardSize = (BoardSize)ibs;\r
1128     }\r
1129   }\r
1130 \r
1131   InitDrawingSizes(boardSize, 0);\r
1132   RecentEngineMenu(appData.recentEngineList);\r
1133   InitMenuChecks();\r
1134   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1135 \r
1136   /* [AS] Load textures if specified */\r
1137   InitTextures();\r
1138 \r
1139   mysrandom( (unsigned) time(NULL) );\r
1140 \r
1141   /* [AS] Restore layout */\r
1142   if( wpMoveHistory.visible ) {\r
1143       MoveHistoryPopUp();\r
1144   }\r
1145 \r
1146   if( wpEvalGraph.visible ) {\r
1147       EvalGraphPopUp();\r
1148   }\r
1149 \r
1150   if( wpEngineOutput.visible ) {\r
1151       EngineOutputPopUp();\r
1152   }\r
1153 \r
1154   /* Make the window visible; update its client area; and return "success" */\r
1155   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1156   wp.length = sizeof(WINDOWPLACEMENT);\r
1157   wp.flags = 0;\r
1158   wp.showCmd = nCmdShow;\r
1159   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1160   wp.rcNormalPosition.left = wpMain.x;\r
1161   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1162   wp.rcNormalPosition.top = wpMain.y;\r
1163   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1164   SetWindowPlacement(hwndMain, &wp);\r
1165 \r
1166   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1167 \r
1168   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1169                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1170 \r
1171   if (hwndConsole) {\r
1172 #if AOT_CONSOLE\r
1173     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1174                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1175 #endif\r
1176     ShowWindow(hwndConsole, nCmdShow);\r
1177     SetActiveWindow(hwndConsole);\r
1178   }\r
1179   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1180   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1181 \r
1182   return TRUE;\r
1183 \r
1184 }\r
1185 \r
1186 VOID\r
1187 InitMenuChecks()\r
1188 {\r
1189   HMENU hmenu = GetMenu(hwndMain);\r
1190 \r
1191   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1192                         MF_BYCOMMAND|((appData.icsActive &&\r
1193                                        *appData.icsCommPort != NULLCHAR) ?\r
1194                                       MF_ENABLED : MF_GRAYED));\r
1195   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1196                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1197                                      MF_CHECKED : MF_UNCHECKED));\r
1198 }\r
1199 \r
1200 //---------------------------------------------------------------------------------------------------------\r
1201 \r
1202 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1203 #define XBOARD FALSE\r
1204 \r
1205 #define OPTCHAR "/"\r
1206 #define SEPCHAR "="\r
1207 #define TOPLEVEL 0\r
1208 \r
1209 #include "args.h"\r
1210 \r
1211 // front-end part of option handling\r
1212 \r
1213 VOID\r
1214 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1215 {\r
1216   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1217   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1218   DeleteDC(hdc);\r
1219   lf->lfWidth = 0;\r
1220   lf->lfEscapement = 0;\r
1221   lf->lfOrientation = 0;\r
1222   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1223   lf->lfItalic = mfp->italic;\r
1224   lf->lfUnderline = mfp->underline;\r
1225   lf->lfStrikeOut = mfp->strikeout;\r
1226   lf->lfCharSet = mfp->charset;\r
1227   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1228   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1229   lf->lfQuality = DEFAULT_QUALITY;\r
1230   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1231     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1232 }\r
1233 \r
1234 void\r
1235 CreateFontInMF(MyFont *mf)\r
1236\r
1237   LFfromMFP(&mf->lf, &mf->mfp);\r
1238   if (mf->hf) DeleteObject(mf->hf);\r
1239   mf->hf = CreateFontIndirect(&mf->lf);\r
1240 }\r
1241 \r
1242 // [HGM] This platform-dependent table provides the location for storing the color info\r
1243 void *\r
1244 colorVariable[] = {\r
1245   &whitePieceColor, \r
1246   &blackPieceColor, \r
1247   &lightSquareColor,\r
1248   &darkSquareColor, \r
1249   &highlightSquareColor,\r
1250   &premoveHighlightColor,\r
1251   NULL,\r
1252   &consoleBackgroundColor,\r
1253   &appData.fontForeColorWhite,\r
1254   &appData.fontBackColorWhite,\r
1255   &appData.fontForeColorBlack,\r
1256   &appData.fontBackColorBlack,\r
1257   &appData.evalHistColorWhite,\r
1258   &appData.evalHistColorBlack,\r
1259   &appData.highlightArrowColor,\r
1260 };\r
1261 \r
1262 /* Command line font name parser.  NULL name means do nothing.\r
1263    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1264    For backward compatibility, syntax without the colon is also\r
1265    accepted, but font names with digits in them won't work in that case.\r
1266 */\r
1267 VOID\r
1268 ParseFontName(char *name, MyFontParams *mfp)\r
1269 {\r
1270   char *p, *q;\r
1271   if (name == NULL) return;\r
1272   p = name;\r
1273   q = strchr(p, ':');\r
1274   if (q) {\r
1275     if (q - p >= sizeof(mfp->faceName))\r
1276       ExitArgError(_("Font name too long:"), name, TRUE);\r
1277     memcpy(mfp->faceName, p, q - p);\r
1278     mfp->faceName[q - p] = NULLCHAR;\r
1279     p = q + 1;\r
1280   } else {\r
1281     q = mfp->faceName;\r
1282     while (*p && !isdigit(*p)) {\r
1283       *q++ = *p++;\r
1284       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1285         ExitArgError(_("Font name too long:"), name, TRUE);\r
1286     }\r
1287     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1288     *q = NULLCHAR;\r
1289   }\r
1290   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1291   mfp->pointSize = (float) atof(p);\r
1292   mfp->bold = (strchr(p, 'b') != NULL);\r
1293   mfp->italic = (strchr(p, 'i') != NULL);\r
1294   mfp->underline = (strchr(p, 'u') != NULL);\r
1295   mfp->strikeout = (strchr(p, 's') != NULL);\r
1296   mfp->charset = DEFAULT_CHARSET;\r
1297   q = strchr(p, 'c');\r
1298   if (q)\r
1299     mfp->charset = (BYTE) atoi(q+1);\r
1300 }\r
1301 \r
1302 void\r
1303 ParseFont(char *name, int number)\r
1304 { // wrapper to shield back-end from 'font'\r
1305   ParseFontName(name, &font[boardSize][number]->mfp);\r
1306 }\r
1307 \r
1308 void\r
1309 SetFontDefaults()\r
1310 { // in WB  we have a 2D array of fonts; this initializes their description\r
1311   int i, j;\r
1312   /* Point font array elements to structures and\r
1313      parse default font names */\r
1314   for (i=0; i<NUM_FONTS; i++) {\r
1315     for (j=0; j<NUM_SIZES; j++) {\r
1316       font[j][i] = &fontRec[j][i];\r
1317       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1318     }\r
1319   }\r
1320 }\r
1321 \r
1322 void\r
1323 CreateFonts()\r
1324 { // here we create the actual fonts from the selected descriptions\r
1325   int i, j;\r
1326   for (i=0; i<NUM_FONTS; i++) {\r
1327     for (j=0; j<NUM_SIZES; j++) {\r
1328       CreateFontInMF(font[j][i]);\r
1329     }\r
1330   }\r
1331 }\r
1332 /* Color name parser.\r
1333    X version accepts X color names, but this one\r
1334    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1335 COLORREF\r
1336 ParseColorName(char *name)\r
1337 {\r
1338   int red, green, blue, count;\r
1339   char buf[MSG_SIZ];\r
1340 \r
1341   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1342   if (count != 3) {\r
1343     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1344       &red, &green, &blue);\r
1345   }\r
1346   if (count != 3) {\r
1347     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1348     DisplayError(buf, 0);\r
1349     return RGB(0, 0, 0);\r
1350   }\r
1351   return PALETTERGB(red, green, blue);\r
1352 }\r
1353 \r
1354 void\r
1355 ParseColor(int n, char *name)\r
1356 { // for WinBoard the color is an int, which needs to be derived from the string\r
1357   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1358 }\r
1359 \r
1360 void\r
1361 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1362 {\r
1363   char *e = argValue;\r
1364   int eff = 0;\r
1365 \r
1366   while (*e) {\r
1367     if (*e == 'b')      eff |= CFE_BOLD;\r
1368     else if (*e == 'i') eff |= CFE_ITALIC;\r
1369     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1370     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1371     else if (*e == '#' || isdigit(*e)) break;\r
1372     e++;\r
1373   }\r
1374   *effects = eff;\r
1375   *color   = ParseColorName(e);\r
1376 }\r
1377 \r
1378 void\r
1379 ParseTextAttribs(ColorClass cc, char *s)\r
1380 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1381     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1382     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1383 }\r
1384 \r
1385 void\r
1386 ParseBoardSize(void *addr, char *name)\r
1387 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1388   BoardSize bs = SizeTiny;\r
1389   while (sizeInfo[bs].name != NULL) {\r
1390     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1391         *(BoardSize *)addr = bs;\r
1392         return;\r
1393     }\r
1394     bs++;\r
1395   }\r
1396   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1397 }\r
1398 \r
1399 void\r
1400 LoadAllSounds()\r
1401 { // [HGM] import name from appData first\r
1402   ColorClass cc;\r
1403   SoundClass sc;\r
1404   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1405     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1406     textAttribs[cc].sound.data = NULL;\r
1407     MyLoadSound(&textAttribs[cc].sound);\r
1408   }\r
1409   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1410     textAttribs[cc].sound.name = strdup("");\r
1411     textAttribs[cc].sound.data = NULL;\r
1412   }\r
1413   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1414     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1415     sounds[sc].data = NULL;\r
1416     MyLoadSound(&sounds[sc]);\r
1417   }\r
1418 }\r
1419 \r
1420 void\r
1421 SetCommPortDefaults()\r
1422 {\r
1423    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1424   dcb.DCBlength = sizeof(DCB);\r
1425   dcb.BaudRate = 9600;\r
1426   dcb.fBinary = TRUE;\r
1427   dcb.fParity = FALSE;\r
1428   dcb.fOutxCtsFlow = FALSE;\r
1429   dcb.fOutxDsrFlow = FALSE;\r
1430   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1431   dcb.fDsrSensitivity = FALSE;\r
1432   dcb.fTXContinueOnXoff = TRUE;\r
1433   dcb.fOutX = FALSE;\r
1434   dcb.fInX = FALSE;\r
1435   dcb.fNull = FALSE;\r
1436   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1437   dcb.fAbortOnError = FALSE;\r
1438   dcb.ByteSize = 7;\r
1439   dcb.Parity = SPACEPARITY;\r
1440   dcb.StopBits = ONESTOPBIT;\r
1441 }\r
1442 \r
1443 // [HGM] args: these three cases taken out to stay in front-end\r
1444 void\r
1445 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1446 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1447         // while the curent board size determines the element. This system should be ported to XBoard.\r
1448         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1449         int bs;\r
1450         for (bs=0; bs<NUM_SIZES; bs++) {\r
1451           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1452           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1453           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1454             ad->argName, mfp->faceName, mfp->pointSize,\r
1455             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1456             mfp->bold ? "b" : "",\r
1457             mfp->italic ? "i" : "",\r
1458             mfp->underline ? "u" : "",\r
1459             mfp->strikeout ? "s" : "",\r
1460             (int)mfp->charset);\r
1461         }\r
1462       }\r
1463 \r
1464 void\r
1465 ExportSounds()\r
1466 { // [HGM] copy the names from the internal WB variables to appData\r
1467   ColorClass cc;\r
1468   SoundClass sc;\r
1469   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1470     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1471   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1472     (&appData.soundMove)[sc] = sounds[sc].name;\r
1473 }\r
1474 \r
1475 void\r
1476 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1477 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1478         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1479         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1480           (ta->effects & CFE_BOLD) ? "b" : "",\r
1481           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1482           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1483           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1484           (ta->effects) ? " " : "",\r
1485           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1486       }\r
1487 \r
1488 void\r
1489 SaveColor(FILE *f, ArgDescriptor *ad)\r
1490 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1491         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1492         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1493           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1494 }\r
1495 \r
1496 void\r
1497 SaveBoardSize(FILE *f, char *name, void *addr)\r
1498 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1499   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1500 }\r
1501 \r
1502 void\r
1503 ParseCommPortSettings(char *s)\r
1504 { // wrapper to keep dcb from back-end\r
1505   ParseCommSettings(s, &dcb);\r
1506 }\r
1507 \r
1508 void\r
1509 GetWindowCoords()\r
1510 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1511   GetActualPlacement(hwndMain, &wpMain);\r
1512   GetActualPlacement(hwndConsole, &wpConsole);\r
1513   GetActualPlacement(commentDialog, &wpComment);\r
1514   GetActualPlacement(editTagsDialog, &wpTags);\r
1515   GetActualPlacement(gameListDialog, &wpGameList);\r
1516   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1517   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1518   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1519 }\r
1520 \r
1521 void\r
1522 PrintCommPortSettings(FILE *f, char *name)\r
1523 { // wrapper to shield back-end from DCB\r
1524       PrintCommSettings(f, name, &dcb);\r
1525 }\r
1526 \r
1527 int\r
1528 MySearchPath(char *installDir, char *name, char *fullname)\r
1529 {\r
1530   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1531   if(name[0]== '%') {\r
1532     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1533     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1534       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1535       *strchr(buf, '%') = 0;\r
1536       strcat(fullname, getenv(buf));\r
1537       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1538     }\r
1539     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1540     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1541     return (int) strlen(fullname);\r
1542   }\r
1543   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1544 }\r
1545 \r
1546 int\r
1547 MyGetFullPathName(char *name, char *fullname)\r
1548 {\r
1549   char *dummy;\r
1550   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1551 }\r
1552 \r
1553 int\r
1554 MainWindowUp()\r
1555 { // [HGM] args: allows testing if main window is realized from back-end\r
1556   return hwndMain != NULL;\r
1557 }\r
1558 \r
1559 void\r
1560 PopUpStartupDialog()\r
1561 {\r
1562     FARPROC lpProc;\r
1563     \r
1564     LoadLanguageFile(appData.language);\r
1565     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1566     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1567     FreeProcInstance(lpProc);\r
1568 }\r
1569 \r
1570 /*---------------------------------------------------------------------------*\\r
1571  *\r
1572  * GDI board drawing routines\r
1573  *\r
1574 \*---------------------------------------------------------------------------*/\r
1575 \r
1576 /* [AS] Draw square using background texture */\r
1577 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1578 {\r
1579     XFORM   x;\r
1580 \r
1581     if( mode == 0 ) {\r
1582         return; /* Should never happen! */\r
1583     }\r
1584 \r
1585     SetGraphicsMode( dst, GM_ADVANCED );\r
1586 \r
1587     switch( mode ) {\r
1588     case 1:\r
1589         /* Identity */\r
1590         break;\r
1591     case 2:\r
1592         /* X reflection */\r
1593         x.eM11 = -1.0;\r
1594         x.eM12 = 0;\r
1595         x.eM21 = 0;\r
1596         x.eM22 = 1.0;\r
1597         x.eDx = (FLOAT) dw + dx - 1;\r
1598         x.eDy = 0;\r
1599         dx = 0;\r
1600         SetWorldTransform( dst, &x );\r
1601         break;\r
1602     case 3:\r
1603         /* Y reflection */\r
1604         x.eM11 = 1.0;\r
1605         x.eM12 = 0;\r
1606         x.eM21 = 0;\r
1607         x.eM22 = -1.0;\r
1608         x.eDx = 0;\r
1609         x.eDy = (FLOAT) dh + dy - 1;\r
1610         dy = 0;\r
1611         SetWorldTransform( dst, &x );\r
1612         break;\r
1613     case 4:\r
1614         /* X/Y flip */\r
1615         x.eM11 = 0;\r
1616         x.eM12 = 1.0;\r
1617         x.eM21 = 1.0;\r
1618         x.eM22 = 0;\r
1619         x.eDx = (FLOAT) dx;\r
1620         x.eDy = (FLOAT) dy;\r
1621         dx = 0;\r
1622         dy = 0;\r
1623         SetWorldTransform( dst, &x );\r
1624         break;\r
1625     }\r
1626 \r
1627     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1628 \r
1629     x.eM11 = 1.0;\r
1630     x.eM12 = 0;\r
1631     x.eM21 = 0;\r
1632     x.eM22 = 1.0;\r
1633     x.eDx = 0;\r
1634     x.eDy = 0;\r
1635     SetWorldTransform( dst, &x );\r
1636 \r
1637     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1638 }\r
1639 \r
1640 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1641 enum {\r
1642     PM_WP = (int) WhitePawn, \r
1643     PM_WN = (int) WhiteKnight, \r
1644     PM_WB = (int) WhiteBishop, \r
1645     PM_WR = (int) WhiteRook, \r
1646     PM_WQ = (int) WhiteQueen, \r
1647     PM_WF = (int) WhiteFerz, \r
1648     PM_WW = (int) WhiteWazir, \r
1649     PM_WE = (int) WhiteAlfil, \r
1650     PM_WM = (int) WhiteMan, \r
1651     PM_WO = (int) WhiteCannon, \r
1652     PM_WU = (int) WhiteUnicorn, \r
1653     PM_WH = (int) WhiteNightrider, \r
1654     PM_WA = (int) WhiteAngel, \r
1655     PM_WC = (int) WhiteMarshall, \r
1656     PM_WAB = (int) WhiteCardinal, \r
1657     PM_WD = (int) WhiteDragon, \r
1658     PM_WL = (int) WhiteLance, \r
1659     PM_WS = (int) WhiteCobra, \r
1660     PM_WV = (int) WhiteFalcon, \r
1661     PM_WSG = (int) WhiteSilver, \r
1662     PM_WG = (int) WhiteGrasshopper, \r
1663     PM_WK = (int) WhiteKing,\r
1664     PM_BP = (int) BlackPawn, \r
1665     PM_BN = (int) BlackKnight, \r
1666     PM_BB = (int) BlackBishop, \r
1667     PM_BR = (int) BlackRook, \r
1668     PM_BQ = (int) BlackQueen, \r
1669     PM_BF = (int) BlackFerz, \r
1670     PM_BW = (int) BlackWazir, \r
1671     PM_BE = (int) BlackAlfil, \r
1672     PM_BM = (int) BlackMan,\r
1673     PM_BO = (int) BlackCannon, \r
1674     PM_BU = (int) BlackUnicorn, \r
1675     PM_BH = (int) BlackNightrider, \r
1676     PM_BA = (int) BlackAngel, \r
1677     PM_BC = (int) BlackMarshall, \r
1678     PM_BG = (int) BlackGrasshopper, \r
1679     PM_BAB = (int) BlackCardinal,\r
1680     PM_BD = (int) BlackDragon,\r
1681     PM_BL = (int) BlackLance,\r
1682     PM_BS = (int) BlackCobra,\r
1683     PM_BV = (int) BlackFalcon,\r
1684     PM_BSG = (int) BlackSilver,\r
1685     PM_BK = (int) BlackKing\r
1686 };\r
1687 \r
1688 static HFONT hPieceFont = NULL;\r
1689 static HBITMAP hPieceMask[(int) EmptySquare];\r
1690 static HBITMAP hPieceFace[(int) EmptySquare];\r
1691 static int fontBitmapSquareSize = 0;\r
1692 static char pieceToFontChar[(int) EmptySquare] =\r
1693                               { 'p', 'n', 'b', 'r', 'q', \r
1694                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1695                       'k', 'o', 'm', 'v', 't', 'w', \r
1696                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1697                                                               'l' };\r
1698 \r
1699 extern BOOL SetCharTable( char *table, const char * map );\r
1700 /* [HGM] moved to backend.c */\r
1701 \r
1702 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1703 {\r
1704     HBRUSH hbrush;\r
1705     BYTE r1 = GetRValue( color );\r
1706     BYTE g1 = GetGValue( color );\r
1707     BYTE b1 = GetBValue( color );\r
1708     BYTE r2 = r1 / 2;\r
1709     BYTE g2 = g1 / 2;\r
1710     BYTE b2 = b1 / 2;\r
1711     RECT rc;\r
1712 \r
1713     /* Create a uniform background first */\r
1714     hbrush = CreateSolidBrush( color );\r
1715     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1716     FillRect( hdc, &rc, hbrush );\r
1717     DeleteObject( hbrush );\r
1718     \r
1719     if( mode == 1 ) {\r
1720         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1721         int steps = squareSize / 2;\r
1722         int i;\r
1723 \r
1724         for( i=0; i<steps; i++ ) {\r
1725             BYTE r = r1 - (r1-r2) * i / steps;\r
1726             BYTE g = g1 - (g1-g2) * i / steps;\r
1727             BYTE b = b1 - (b1-b2) * i / steps;\r
1728 \r
1729             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1730             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1731             FillRect( hdc, &rc, hbrush );\r
1732             DeleteObject(hbrush);\r
1733         }\r
1734     }\r
1735     else if( mode == 2 ) {\r
1736         /* Diagonal gradient, good more or less for every piece */\r
1737         POINT triangle[3];\r
1738         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1739         HBRUSH hbrush_old;\r
1740         int steps = squareSize;\r
1741         int i;\r
1742 \r
1743         triangle[0].x = squareSize - steps;\r
1744         triangle[0].y = squareSize;\r
1745         triangle[1].x = squareSize;\r
1746         triangle[1].y = squareSize;\r
1747         triangle[2].x = squareSize;\r
1748         triangle[2].y = squareSize - steps;\r
1749 \r
1750         for( i=0; i<steps; i++ ) {\r
1751             BYTE r = r1 - (r1-r2) * i / steps;\r
1752             BYTE g = g1 - (g1-g2) * i / steps;\r
1753             BYTE b = b1 - (b1-b2) * i / steps;\r
1754 \r
1755             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1756             hbrush_old = SelectObject( hdc, hbrush );\r
1757             Polygon( hdc, triangle, 3 );\r
1758             SelectObject( hdc, hbrush_old );\r
1759             DeleteObject(hbrush);\r
1760             triangle[0].x++;\r
1761             triangle[2].y++;\r
1762         }\r
1763 \r
1764         SelectObject( hdc, hpen );\r
1765     }\r
1766 }\r
1767 \r
1768 /*\r
1769     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1770     seems to work ok. The main problem here is to find the "inside" of a chess\r
1771     piece: follow the steps as explained below.\r
1772 */\r
1773 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1774 {\r
1775     HBITMAP hbm;\r
1776     HBITMAP hbm_old;\r
1777     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1778     RECT rc;\r
1779     SIZE sz;\r
1780     POINT pt;\r
1781     int backColor = whitePieceColor; \r
1782     int foreColor = blackPieceColor;\r
1783     \r
1784     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1785         backColor = appData.fontBackColorWhite;\r
1786         foreColor = appData.fontForeColorWhite;\r
1787     }\r
1788     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1789         backColor = appData.fontBackColorBlack;\r
1790         foreColor = appData.fontForeColorBlack;\r
1791     }\r
1792 \r
1793     /* Mask */\r
1794     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1795 \r
1796     hbm_old = SelectObject( hdc, hbm );\r
1797 \r
1798     rc.left = 0;\r
1799     rc.top = 0;\r
1800     rc.right = squareSize;\r
1801     rc.bottom = squareSize;\r
1802 \r
1803     /* Step 1: background is now black */\r
1804     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1805 \r
1806     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1807 \r
1808     pt.x = (squareSize - sz.cx) / 2;\r
1809     pt.y = (squareSize - sz.cy) / 2;\r
1810 \r
1811     SetBkMode( hdc, TRANSPARENT );\r
1812     SetTextColor( hdc, chroma );\r
1813     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1814     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1815 \r
1816     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1817     /* Step 3: the area outside the piece is filled with white */\r
1818 //    FloodFill( hdc, 0, 0, chroma );\r
1819     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1820     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1821     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1822     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1823     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1824     /* \r
1825         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1826         but if the start point is not inside the piece we're lost!\r
1827         There should be a better way to do this... if we could create a region or path\r
1828         from the fill operation we would be fine for example.\r
1829     */\r
1830 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1831     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1832 \r
1833     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1834         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1835         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1836 \r
1837         SelectObject( dc2, bm2 );\r
1838         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1839         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1840         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1841         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1842         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1843 \r
1844         DeleteDC( dc2 );\r
1845         DeleteObject( bm2 );\r
1846     }\r
1847 \r
1848     SetTextColor( hdc, 0 );\r
1849     /* \r
1850         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1851         draw the piece again in black for safety.\r
1852     */\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( hPieceMask[index] != NULL ) {\r
1858         DeleteObject( hPieceMask[index] );\r
1859     }\r
1860 \r
1861     hPieceMask[index] = hbm;\r
1862 \r
1863     /* Face */\r
1864     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1865 \r
1866     SelectObject( hdc, hbm );\r
1867 \r
1868     {\r
1869         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1870         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1871         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873         SelectObject( dc1, hPieceMask[index] );\r
1874         SelectObject( dc2, bm2 );\r
1875         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1876         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1877         \r
1878         /* \r
1879             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1880             the piece background and deletes (makes transparent) the rest.\r
1881             Thanks to that mask, we are free to paint the background with the greates\r
1882             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1883             We use this, to make gradients and give the pieces a "roundish" look.\r
1884         */\r
1885         SetPieceBackground( hdc, backColor, 2 );\r
1886         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1887 \r
1888         DeleteDC( dc2 );\r
1889         DeleteDC( dc1 );\r
1890         DeleteObject( bm2 );\r
1891     }\r
1892 \r
1893     SetTextColor( hdc, foreColor );\r
1894     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1895 \r
1896     SelectObject( hdc, hbm_old );\r
1897 \r
1898     if( hPieceFace[index] != NULL ) {\r
1899         DeleteObject( hPieceFace[index] );\r
1900     }\r
1901 \r
1902     hPieceFace[index] = hbm;\r
1903 }\r
1904 \r
1905 static int TranslatePieceToFontPiece( int piece )\r
1906 {\r
1907     switch( piece ) {\r
1908     case BlackPawn:\r
1909         return PM_BP;\r
1910     case BlackKnight:\r
1911         return PM_BN;\r
1912     case BlackBishop:\r
1913         return PM_BB;\r
1914     case BlackRook:\r
1915         return PM_BR;\r
1916     case BlackQueen:\r
1917         return PM_BQ;\r
1918     case BlackKing:\r
1919         return PM_BK;\r
1920     case WhitePawn:\r
1921         return PM_WP;\r
1922     case WhiteKnight:\r
1923         return PM_WN;\r
1924     case WhiteBishop:\r
1925         return PM_WB;\r
1926     case WhiteRook:\r
1927         return PM_WR;\r
1928     case WhiteQueen:\r
1929         return PM_WQ;\r
1930     case WhiteKing:\r
1931         return PM_WK;\r
1932 \r
1933     case BlackAngel:\r
1934         return PM_BA;\r
1935     case BlackMarshall:\r
1936         return PM_BC;\r
1937     case BlackFerz:\r
1938         return PM_BF;\r
1939     case BlackNightrider:\r
1940         return PM_BH;\r
1941     case BlackAlfil:\r
1942         return PM_BE;\r
1943     case BlackWazir:\r
1944         return PM_BW;\r
1945     case BlackUnicorn:\r
1946         return PM_BU;\r
1947     case BlackCannon:\r
1948         return PM_BO;\r
1949     case BlackGrasshopper:\r
1950         return PM_BG;\r
1951     case BlackMan:\r
1952         return PM_BM;\r
1953     case BlackSilver:\r
1954         return PM_BSG;\r
1955     case BlackLance:\r
1956         return PM_BL;\r
1957     case BlackFalcon:\r
1958         return PM_BV;\r
1959     case BlackCobra:\r
1960         return PM_BS;\r
1961     case BlackCardinal:\r
1962         return PM_BAB;\r
1963     case BlackDragon:\r
1964         return PM_BD;\r
1965 \r
1966     case WhiteAngel:\r
1967         return PM_WA;\r
1968     case WhiteMarshall:\r
1969         return PM_WC;\r
1970     case WhiteFerz:\r
1971         return PM_WF;\r
1972     case WhiteNightrider:\r
1973         return PM_WH;\r
1974     case WhiteAlfil:\r
1975         return PM_WE;\r
1976     case WhiteWazir:\r
1977         return PM_WW;\r
1978     case WhiteUnicorn:\r
1979         return PM_WU;\r
1980     case WhiteCannon:\r
1981         return PM_WO;\r
1982     case WhiteGrasshopper:\r
1983         return PM_WG;\r
1984     case WhiteMan:\r
1985         return PM_WM;\r
1986     case WhiteSilver:\r
1987         return PM_WSG;\r
1988     case WhiteLance:\r
1989         return PM_WL;\r
1990     case WhiteFalcon:\r
1991         return PM_WV;\r
1992     case WhiteCobra:\r
1993         return PM_WS;\r
1994     case WhiteCardinal:\r
1995         return PM_WAB;\r
1996     case WhiteDragon:\r
1997         return PM_WD;\r
1998     }\r
1999 \r
2000     return 0;\r
2001 }\r
2002 \r
2003 void CreatePiecesFromFont()\r
2004 {\r
2005     LOGFONT lf;\r
2006     HDC hdc_window = NULL;\r
2007     HDC hdc = NULL;\r
2008     HFONT hfont_old;\r
2009     int fontHeight;\r
2010     int i;\r
2011 \r
2012     if( fontBitmapSquareSize < 0 ) {\r
2013         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2014         return;\r
2015     }\r
2016 \r
2017     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2018             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2019         fontBitmapSquareSize = -1;\r
2020         return;\r
2021     }\r
2022 \r
2023     if( fontBitmapSquareSize != squareSize ) {\r
2024         hdc_window = GetDC( hwndMain );\r
2025         hdc = CreateCompatibleDC( hdc_window );\r
2026 \r
2027         if( hPieceFont != NULL ) {\r
2028             DeleteObject( hPieceFont );\r
2029         }\r
2030         else {\r
2031             for( i=0; i<=(int)BlackKing; i++ ) {\r
2032                 hPieceMask[i] = NULL;\r
2033                 hPieceFace[i] = NULL;\r
2034             }\r
2035         }\r
2036 \r
2037         fontHeight = 75;\r
2038 \r
2039         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2040             fontHeight = appData.fontPieceSize;\r
2041         }\r
2042 \r
2043         fontHeight = (fontHeight * squareSize) / 100;\r
2044 \r
2045         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2046         lf.lfWidth = 0;\r
2047         lf.lfEscapement = 0;\r
2048         lf.lfOrientation = 0;\r
2049         lf.lfWeight = FW_NORMAL;\r
2050         lf.lfItalic = 0;\r
2051         lf.lfUnderline = 0;\r
2052         lf.lfStrikeOut = 0;\r
2053         lf.lfCharSet = DEFAULT_CHARSET;\r
2054         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2055         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2056         lf.lfQuality = PROOF_QUALITY;\r
2057         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2058         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2059         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2060 \r
2061         hPieceFont = CreateFontIndirect( &lf );\r
2062 \r
2063         if( hPieceFont == NULL ) {\r
2064             fontBitmapSquareSize = -2;\r
2065         }\r
2066         else {\r
2067             /* Setup font-to-piece character table */\r
2068             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2069                 /* No (or wrong) global settings, try to detect the font */\r
2070                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2071                     /* Alpha */\r
2072                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2073                 }\r
2074                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2075                     /* DiagramTT* family */\r
2076                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2077                 }\r
2078                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2079                     /* Fairy symbols */\r
2080                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2081                 }\r
2082                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2083                     /* Good Companion (Some characters get warped as literal :-( */\r
2084                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2085                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2086                     SetCharTable(pieceToFontChar, s);\r
2087                 }\r
2088                 else {\r
2089                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2090                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2091                 }\r
2092             }\r
2093 \r
2094             /* Create bitmaps */\r
2095             hfont_old = SelectObject( hdc, hPieceFont );\r
2096             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2097                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2098                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2099 \r
2100             SelectObject( hdc, hfont_old );\r
2101 \r
2102             fontBitmapSquareSize = squareSize;\r
2103         }\r
2104     }\r
2105 \r
2106     if( hdc != NULL ) {\r
2107         DeleteDC( hdc );\r
2108     }\r
2109 \r
2110     if( hdc_window != NULL ) {\r
2111         ReleaseDC( hwndMain, hdc_window );\r
2112     }\r
2113 }\r
2114 \r
2115 HBITMAP\r
2116 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2117 {\r
2118   char name[128], buf[MSG_SIZ];\r
2119 \r
2120     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2121   if(appData.pieceDirectory[0]) {\r
2122     HBITMAP res;\r
2123     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2124     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2125     if(res) return res;\r
2126   }\r
2127   if (gameInfo.event &&\r
2128       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2129       strcmp(name, "k80s") == 0) {\r
2130     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2131   }\r
2132   return LoadBitmap(hinst, name);\r
2133 }\r
2134 \r
2135 \r
2136 /* Insert a color into the program's logical palette\r
2137    structure.  This code assumes the given color is\r
2138    the result of the RGB or PALETTERGB macro, and it\r
2139    knows how those macros work (which is documented).\r
2140 */\r
2141 VOID\r
2142 InsertInPalette(COLORREF color)\r
2143 {\r
2144   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2145 \r
2146   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2147     DisplayFatalError(_("Too many colors"), 0, 1);\r
2148     pLogPal->palNumEntries--;\r
2149     return;\r
2150   }\r
2151 \r
2152   pe->peFlags = (char) 0;\r
2153   pe->peRed = (char) (0xFF & color);\r
2154   pe->peGreen = (char) (0xFF & (color >> 8));\r
2155   pe->peBlue = (char) (0xFF & (color >> 16));\r
2156   return;\r
2157 }\r
2158 \r
2159 \r
2160 VOID\r
2161 InitDrawingColors()\r
2162 {\r
2163   if (pLogPal == NULL) {\r
2164     /* Allocate enough memory for a logical palette with\r
2165      * PALETTESIZE entries and set the size and version fields\r
2166      * of the logical palette structure.\r
2167      */\r
2168     pLogPal = (NPLOGPALETTE)\r
2169       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2170                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2171     pLogPal->palVersion    = 0x300;\r
2172   }\r
2173   pLogPal->palNumEntries = 0;\r
2174 \r
2175   InsertInPalette(lightSquareColor);\r
2176   InsertInPalette(darkSquareColor);\r
2177   InsertInPalette(whitePieceColor);\r
2178   InsertInPalette(blackPieceColor);\r
2179   InsertInPalette(highlightSquareColor);\r
2180   InsertInPalette(premoveHighlightColor);\r
2181 \r
2182   /*  create a logical color palette according the information\r
2183    *  in the LOGPALETTE structure.\r
2184    */\r
2185   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2186 \r
2187   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2188   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2189   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2190   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2191   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2192   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2193   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2194   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2195   /* [AS] Force rendering of the font-based pieces */\r
2196   if( fontBitmapSquareSize > 0 ) {\r
2197     fontBitmapSquareSize = 0;\r
2198   }\r
2199 }\r
2200 \r
2201 \r
2202 int\r
2203 BoardWidth(int boardSize, int n)\r
2204 { /* [HGM] argument n added to allow different width and height */\r
2205   int lineGap = sizeInfo[boardSize].lineGap;\r
2206 \r
2207   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2208       lineGap = appData.overrideLineGap;\r
2209   }\r
2210 \r
2211   return (n + 1) * lineGap +\r
2212           n * sizeInfo[boardSize].squareSize;\r
2213 }\r
2214 \r
2215 /* Respond to board resize by dragging edge */\r
2216 VOID\r
2217 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2218 {\r
2219   BoardSize newSize = NUM_SIZES - 1;\r
2220   static int recurse = 0;\r
2221   if (IsIconic(hwndMain)) return;\r
2222   if (recurse > 0) return;\r
2223   recurse++;\r
2224   while (newSize > 0) {\r
2225         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2226         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2227            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2228     newSize--;\r
2229   } \r
2230   boardSize = newSize;\r
2231   InitDrawingSizes(boardSize, flags);\r
2232   recurse--;\r
2233 }\r
2234 \r
2235 \r
2236 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2237 \r
2238 VOID\r
2239 InitDrawingSizes(BoardSize boardSize, int flags)\r
2240 {\r
2241   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2242   ChessSquare piece;\r
2243   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2244   HDC hdc;\r
2245   SIZE clockSize, messageSize;\r
2246   HFONT oldFont;\r
2247   char buf[MSG_SIZ];\r
2248   char *str;\r
2249   HMENU hmenu = GetMenu(hwndMain);\r
2250   RECT crect, wrect, oldRect;\r
2251   int offby;\r
2252   LOGBRUSH logbrush;\r
2253   VariantClass v = gameInfo.variant;\r
2254 \r
2255   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2256   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2257 \r
2258   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2259   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2260   oldBoardSize = boardSize;\r
2261 \r
2262   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2263   { // correct board size to one where built-in pieces exist\r
2264     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2265        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2266       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2267       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2268       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2269       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2270       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2271                                    boardSize = SizeMiddling;\r
2272     }\r
2273   }\r
2274   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2275 \r
2276   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2277   oldRect.top = wpMain.y;\r
2278   oldRect.right = wpMain.x + wpMain.width;\r
2279   oldRect.bottom = wpMain.y + wpMain.height;\r
2280 \r
2281   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2282   smallLayout = sizeInfo[boardSize].smallLayout;\r
2283   squareSize = sizeInfo[boardSize].squareSize;\r
2284   lineGap = sizeInfo[boardSize].lineGap;\r
2285   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2286   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2287 \r
2288   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2289       lineGap = appData.overrideLineGap;\r
2290   }\r
2291 \r
2292   if (tinyLayout != oldTinyLayout) {\r
2293     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2294     if (tinyLayout) {\r
2295       style &= ~WS_SYSMENU;\r
2296       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2297                  "&Minimize\tCtrl+F4");\r
2298     } else {\r
2299       style |= WS_SYSMENU;\r
2300       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2301     }\r
2302     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2303 \r
2304     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2305       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2306         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2307     }\r
2308     DrawMenuBar(hwndMain);\r
2309   }\r
2310 \r
2311   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2312   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2313 \r
2314   /* Get text area sizes */\r
2315   hdc = GetDC(hwndMain);\r
2316   if (appData.clockMode) {\r
2317     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2318   } else {\r
2319     snprintf(buf, MSG_SIZ, _("White"));\r
2320   }\r
2321   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2322   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2323   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2324   str = _("We only care about the height here");\r
2325   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2326   SelectObject(hdc, oldFont);\r
2327   ReleaseDC(hwndMain, hdc);\r
2328 \r
2329   /* Compute where everything goes */\r
2330   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2331         /* [HGM] logo: if either logo is on, reserve space for it */\r
2332         logoHeight =  2*clockSize.cy;\r
2333         leftLogoRect.left   = OUTER_MARGIN;\r
2334         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2335         leftLogoRect.top    = OUTER_MARGIN;\r
2336         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2337 \r
2338         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2339         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2340         rightLogoRect.top    = OUTER_MARGIN;\r
2341         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2342 \r
2343 \r
2344     whiteRect.left = leftLogoRect.right;\r
2345     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2346     whiteRect.top = OUTER_MARGIN;\r
2347     whiteRect.bottom = whiteRect.top + logoHeight;\r
2348 \r
2349     blackRect.right = rightLogoRect.left;\r
2350     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2351     blackRect.top = whiteRect.top;\r
2352     blackRect.bottom = whiteRect.bottom;\r
2353   } else {\r
2354     whiteRect.left = OUTER_MARGIN;\r
2355     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2356     whiteRect.top = OUTER_MARGIN;\r
2357     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2358 \r
2359     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2360     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2361     blackRect.top = whiteRect.top;\r
2362     blackRect.bottom = whiteRect.bottom;\r
2363 \r
2364     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2365   }\r
2366 \r
2367   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2368   if (appData.showButtonBar) {\r
2369     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2370       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2371   } else {\r
2372     messageRect.right = OUTER_MARGIN + boardWidth;\r
2373   }\r
2374   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2375   messageRect.bottom = messageRect.top + messageSize.cy;\r
2376 \r
2377   boardRect.left = OUTER_MARGIN;\r
2378   boardRect.right = boardRect.left + boardWidth;\r
2379   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2380   boardRect.bottom = boardRect.top + boardHeight;\r
2381 \r
2382   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2383   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2384   oldTinyLayout = tinyLayout;\r
2385   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2386   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2387     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2388   winW *= 1 + twoBoards;\r
2389   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2390   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2391   wpMain.height = winH; //       without disturbing window attachments\r
2392   GetWindowRect(hwndMain, &wrect);\r
2393   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2394                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2395 \r
2396   // [HGM] placement: let attached windows follow size change.\r
2397   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2398   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2399   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2400   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2401   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2402 \r
2403   /* compensate if menu bar wrapped */\r
2404   GetClientRect(hwndMain, &crect);\r
2405   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2406   wpMain.height += offby;\r
2407   switch (flags) {\r
2408   case WMSZ_TOPLEFT:\r
2409     SetWindowPos(hwndMain, NULL, \r
2410                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2411                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2412     break;\r
2413 \r
2414   case WMSZ_TOPRIGHT:\r
2415   case WMSZ_TOP:\r
2416     SetWindowPos(hwndMain, NULL, \r
2417                  wrect.left, wrect.bottom - wpMain.height, \r
2418                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2419     break;\r
2420 \r
2421   case WMSZ_BOTTOMLEFT:\r
2422   case WMSZ_LEFT:\r
2423     SetWindowPos(hwndMain, NULL, \r
2424                  wrect.right - wpMain.width, wrect.top, \r
2425                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2426     break;\r
2427 \r
2428   case WMSZ_BOTTOMRIGHT:\r
2429   case WMSZ_BOTTOM:\r
2430   case WMSZ_RIGHT:\r
2431   default:\r
2432     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2433                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2434     break;\r
2435   }\r
2436 \r
2437   hwndPause = NULL;\r
2438   for (i = 0; i < N_BUTTONS; i++) {\r
2439     if (buttonDesc[i].hwnd != NULL) {\r
2440       DestroyWindow(buttonDesc[i].hwnd);\r
2441       buttonDesc[i].hwnd = NULL;\r
2442     }\r
2443     if (appData.showButtonBar) {\r
2444       buttonDesc[i].hwnd =\r
2445         CreateWindow("BUTTON", buttonDesc[i].label,\r
2446                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2447                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2448                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2449                      (HMENU) buttonDesc[i].id,\r
2450                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2451       if (tinyLayout) {\r
2452         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2453                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2454                     MAKELPARAM(FALSE, 0));\r
2455       }\r
2456       if (buttonDesc[i].id == IDM_Pause)\r
2457         hwndPause = buttonDesc[i].hwnd;\r
2458       buttonDesc[i].wndproc = (WNDPROC)\r
2459         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2460     }\r
2461   }\r
2462   if (gridPen != NULL) DeleteObject(gridPen);\r
2463   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2464   if (premovePen != NULL) DeleteObject(premovePen);\r
2465   if (lineGap != 0) {\r
2466     logbrush.lbStyle = BS_SOLID;\r
2467     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2468     gridPen =\r
2469       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2470                    lineGap, &logbrush, 0, NULL);\r
2471     logbrush.lbColor = highlightSquareColor;\r
2472     highlightPen =\r
2473       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2474                    lineGap, &logbrush, 0, NULL);\r
2475 \r
2476     logbrush.lbColor = premoveHighlightColor; \r
2477     premovePen =\r
2478       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2479                    lineGap, &logbrush, 0, NULL);\r
2480 \r
2481     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2482     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2483       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2484       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2485         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2486       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2487         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2488       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2489     }\r
2490     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2491       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2492       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2493         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2494         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2495       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2496         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2497       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2498     }\r
2499   }\r
2500 \r
2501   /* [HGM] Licensing requirement */\r
2502 #ifdef GOTHIC\r
2503   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2504 #endif\r
2505 #ifdef FALCON\r
2506   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2507 #endif\r
2508   GothicPopUp( "", VariantNormal);\r
2509 \r
2510 \r
2511 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2512 \r
2513   /* Load piece bitmaps for this board size */\r
2514   for (i=0; i<=2; i++) {\r
2515     for (piece = WhitePawn;\r
2516          (int) piece < (int) BlackPawn;\r
2517          piece = (ChessSquare) ((int) piece + 1)) {\r
2518       if (pieceBitmap[i][piece] != NULL)\r
2519         DeleteObject(pieceBitmap[i][piece]);\r
2520     }\r
2521   }\r
2522 \r
2523   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2524   // Orthodox Chess pieces\r
2525   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2526   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2527   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2528   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2529   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2530   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2531   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2532   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2533   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2534   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2535   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2536   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2537   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2538   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2539   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2540   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2541     // in Shogi, Hijack the unused Queen for Lance\r
2542     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2543     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2544     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2545   } else {\r
2546     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2547     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2548     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2549   }\r
2550 \r
2551   if(squareSize <= 72 && squareSize >= 33) { \r
2552     /* A & C are available in most sizes now */\r
2553     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2554       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2555       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2556       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2557       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2558       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2559       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2560       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2561       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2562       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2563       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2564       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2565       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2566     } else { // Smirf-like\r
2567       if(gameInfo.variant == VariantSChess) {\r
2568         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2569         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2570         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2571       } else {\r
2572         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2573         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2574         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2575       }\r
2576     }\r
2577     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2578       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2579       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2580       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2581     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2582       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2583       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2584       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2585     } else { // WinBoard standard\r
2586       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2587       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2588       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2589     }\r
2590   }\r
2591 \r
2592 \r
2593   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2594     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2595     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2596     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2597     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2598     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2599     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2600     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2601     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2602     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2603     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2604     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2605     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2606     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2607     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2608     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2609     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2610     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2611     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2612     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2613     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2614     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2615     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2616     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2617     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2618     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2619     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2620     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2621     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2622     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2623     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2624 \r
2625     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2626       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2627       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2628       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2629       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2630       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2631       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2632       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2633       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2634       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2635       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2636       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2637       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2638     } else {\r
2639       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2640       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2641       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2642       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2643       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2644       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2645       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2646       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2647       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2648       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2649       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2650       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2651     }\r
2652 \r
2653   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2654     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2655     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2656     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2657     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2658     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2659     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2660     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2661     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2662     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2663     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2664     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2665     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2666     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2667     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2668   }\r
2669 \r
2670 \r
2671   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2672   /* special Shogi support in this size */\r
2673   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2674       for (piece = WhitePawn;\r
2675            (int) piece < (int) BlackPawn;\r
2676            piece = (ChessSquare) ((int) piece + 1)) {\r
2677         if (pieceBitmap[i][piece] != NULL)\r
2678           DeleteObject(pieceBitmap[i][piece]);\r
2679       }\r
2680     }\r
2681   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2682   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2683   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2684   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2685   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2686   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2687   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2688   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2689   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2690   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2691   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2692   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2693   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2694   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2695   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2696   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2697   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2698   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2699   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2700   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2701   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2702   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2703   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2704   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2705   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2706   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2707   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2708   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2709   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2710   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2711   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2712   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2713   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2714   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2715   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2716   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2717   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2718   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2719   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2720   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2721   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2722   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2723   minorSize = 0;\r
2724   }\r
2725 }\r
2726 \r
2727 HBITMAP\r
2728 PieceBitmap(ChessSquare p, int kind)\r
2729 {\r
2730   if ((int) p >= (int) BlackPawn)\r
2731     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2732 \r
2733   return pieceBitmap[kind][(int) p];\r
2734 }\r
2735 \r
2736 /***************************************************************/\r
2737 \r
2738 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2739 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2740 /*\r
2741 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2742 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2743 */\r
2744 \r
2745 VOID\r
2746 SquareToPos(int row, int column, int * x, int * y)\r
2747 {\r
2748   if (flipView) {\r
2749     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2750     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2751   } else {\r
2752     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2753     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2754   }\r
2755 }\r
2756 \r
2757 VOID\r
2758 DrawCoordsOnDC(HDC hdc)\r
2759 {\r
2760   static char files[] = "0123456789012345678901221098765432109876543210";\r
2761   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2762   char str[2] = { NULLCHAR, NULLCHAR };\r
2763   int oldMode, oldAlign, x, y, start, i;\r
2764   HFONT oldFont;\r
2765   HBRUSH oldBrush;\r
2766 \r
2767   if (!appData.showCoords)\r
2768     return;\r
2769 \r
2770   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2771 \r
2772   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2773   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2774   oldAlign = GetTextAlign(hdc);\r
2775   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2776 \r
2777   y = boardRect.top + lineGap;\r
2778   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2779 \r
2780   if(border) {\r
2781     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2782     x += border - lineGap - 4; y += squareSize - 6;\r
2783   } else\r
2784   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2785   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2786     str[0] = files[start + i];\r
2787     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2788     y += squareSize + lineGap;\r
2789   }\r
2790 \r
2791   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2792 \r
2793   if(border) {\r
2794     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2795     x += -border + 4; y += border - squareSize + 6;\r
2796   } else\r
2797   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2798   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2799     str[0] = ranks[start + i];\r
2800     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2801     x += squareSize + lineGap;\r
2802   }    \r
2803 \r
2804   SelectObject(hdc, oldBrush);\r
2805   SetBkMode(hdc, oldMode);\r
2806   SetTextAlign(hdc, oldAlign);\r
2807   SelectObject(hdc, oldFont);\r
2808 }\r
2809 \r
2810 VOID\r
2811 DrawGridOnDC(HDC hdc)\r
2812 {\r
2813   HPEN oldPen;\r
2814  \r
2815   if (lineGap != 0) {\r
2816     oldPen = SelectObject(hdc, gridPen);\r
2817     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2818     SelectObject(hdc, oldPen);\r
2819   }\r
2820 }\r
2821 \r
2822 #define HIGHLIGHT_PEN 0\r
2823 #define PREMOVE_PEN   1\r
2824 \r
2825 VOID\r
2826 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2827 {\r
2828   int x1, y1;\r
2829   HPEN oldPen, hPen;\r
2830   if (lineGap == 0) return;\r
2831   if (flipView) {\r
2832     x1 = boardRect.left +\r
2833       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2834     y1 = boardRect.top +\r
2835       lineGap/2 + y * (squareSize + lineGap) + border;\r
2836   } else {\r
2837     x1 = boardRect.left +\r
2838       lineGap/2 + x * (squareSize + lineGap) + border;\r
2839     y1 = boardRect.top +\r
2840       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2841   }\r
2842   hPen = pen ? premovePen : highlightPen;\r
2843   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2844   MoveToEx(hdc, x1, y1, NULL);\r
2845   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2846   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2847   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2848   LineTo(hdc, x1, y1);\r
2849   SelectObject(hdc, oldPen);\r
2850 }\r
2851 \r
2852 VOID\r
2853 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2854 {\r
2855   int i;\r
2856   for (i=0; i<2; i++) {\r
2857     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2858       DrawHighlightOnDC(hdc, TRUE,\r
2859                         h->sq[i].x, h->sq[i].y,\r
2860                         pen);\r
2861   }\r
2862 }\r
2863 \r
2864 /* Note: sqcolor is used only in monoMode */\r
2865 /* Note that this code is largely duplicated in woptions.c,\r
2866    function DrawSampleSquare, so that needs to be updated too */\r
2867 VOID\r
2868 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2869 {\r
2870   HBITMAP oldBitmap;\r
2871   HBRUSH oldBrush;\r
2872   int tmpSize;\r
2873 \r
2874   if (appData.blindfold) return;\r
2875 \r
2876   /* [AS] Use font-based pieces if needed */\r
2877   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2878     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2879     CreatePiecesFromFont();\r
2880 \r
2881     if( fontBitmapSquareSize == squareSize ) {\r
2882         int index = TranslatePieceToFontPiece(piece);\r
2883 \r
2884         SelectObject( tmphdc, hPieceMask[ index ] );\r
2885 \r
2886       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2887         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2888       else\r
2889         BitBlt( hdc,\r
2890             x, y,\r
2891             squareSize, squareSize,\r
2892             tmphdc,\r
2893             0, 0,\r
2894             SRCAND );\r
2895 \r
2896         SelectObject( tmphdc, hPieceFace[ index ] );\r
2897 \r
2898       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2899         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2900       else\r
2901         BitBlt( hdc,\r
2902             x, y,\r
2903             squareSize, squareSize,\r
2904             tmphdc,\r
2905             0, 0,\r
2906             SRCPAINT );\r
2907 \r
2908         return;\r
2909     }\r
2910   }\r
2911 \r
2912   if (appData.monoMode) {\r
2913     SelectObject(tmphdc, PieceBitmap(piece, \r
2914       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2915     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2916            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2917   } else {\r
2918     HBRUSH xBrush = whitePieceBrush;\r
2919     tmpSize = squareSize;\r
2920     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2921     if(minorSize &&\r
2922         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2923          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2924       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2925       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2926       x += (squareSize - minorSize)>>1;\r
2927       y += squareSize - minorSize - 2;\r
2928       tmpSize = minorSize;\r
2929     }\r
2930     if (color || appData.allWhite ) {\r
2931       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2932       if( color )\r
2933               oldBrush = SelectObject(hdc, xBrush);\r
2934       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2935       if(appData.upsideDown && color==flipView)\r
2936         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2937       else\r
2938         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2939       /* Use black for outline of white pieces */\r
2940       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2941       if(appData.upsideDown && color==flipView)\r
2942         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2943       else\r
2944         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2945     } else if(appData.pieceDirectory[0]) {\r
2946       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2947       oldBrush = SelectObject(hdc, xBrush);\r
2948       if(appData.upsideDown && color==flipView)\r
2949         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2950       else\r
2951         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2952       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2953       if(appData.upsideDown && color==flipView)\r
2954         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2955       else\r
2956         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2957     } else {\r
2958       /* Use square color for details of black pieces */\r
2959       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2960       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2961       if(appData.upsideDown && !flipView)\r
2962         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2963       else\r
2964         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2965     }\r
2966     SelectObject(hdc, oldBrush);\r
2967     SelectObject(tmphdc, oldBitmap);\r
2968   }\r
2969 }\r
2970 \r
2971 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2972 int GetBackTextureMode( int algo )\r
2973 {\r
2974     int result = BACK_TEXTURE_MODE_DISABLED;\r
2975 \r
2976     switch( algo ) \r
2977     {\r
2978         case BACK_TEXTURE_MODE_PLAIN:\r
2979             result = 1; /* Always use identity map */\r
2980             break;\r
2981         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2982             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2983             break;\r
2984     }\r
2985 \r
2986     return result;\r
2987 }\r
2988 \r
2989 /* \r
2990     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2991     to handle redraws cleanly (as random numbers would always be different).\r
2992 */\r
2993 VOID RebuildTextureSquareInfo()\r
2994 {\r
2995     BITMAP bi;\r
2996     int lite_w = 0;\r
2997     int lite_h = 0;\r
2998     int dark_w = 0;\r
2999     int dark_h = 0;\r
3000     int row;\r
3001     int col;\r
3002 \r
3003     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3004 \r
3005     if( liteBackTexture != NULL ) {\r
3006         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3007             lite_w = bi.bmWidth;\r
3008             lite_h = bi.bmHeight;\r
3009         }\r
3010     }\r
3011 \r
3012     if( darkBackTexture != NULL ) {\r
3013         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3014             dark_w = bi.bmWidth;\r
3015             dark_h = bi.bmHeight;\r
3016         }\r
3017     }\r
3018 \r
3019     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3020         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3021             if( (col + row) & 1 ) {\r
3022                 /* Lite square */\r
3023                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3024                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3025                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3026                   else\r
3027                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3028                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3029                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3030                   else\r
3031                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3032                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3033                 }\r
3034             }\r
3035             else {\r
3036                 /* Dark square */\r
3037                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3038                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3039                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3040                   else\r
3041                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3042                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3043                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3044                   else\r
3045                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3046                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3047                 }\r
3048             }\r
3049         }\r
3050     }\r
3051 }\r
3052 \r
3053 /* [AS] Arrow highlighting support */\r
3054 \r
3055 static double A_WIDTH = 5; /* Width of arrow body */\r
3056 \r
3057 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3058 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3059 \r
3060 static double Sqr( double x )\r
3061 {\r
3062     return x*x;\r
3063 }\r
3064 \r
3065 static int Round( double x )\r
3066 {\r
3067     return (int) (x + 0.5);\r
3068 }\r
3069 \r
3070 /* Draw an arrow between two points using current settings */\r
3071 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3072 {\r
3073     POINT arrow[7];\r
3074     double dx, dy, j, k, x, y;\r
3075 \r
3076     if( d_x == s_x ) {\r
3077         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3078 \r
3079         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3080         arrow[0].y = s_y;\r
3081 \r
3082         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3083         arrow[1].y = d_y - h;\r
3084 \r
3085         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3086         arrow[2].y = d_y - h;\r
3087 \r
3088         arrow[3].x = d_x;\r
3089         arrow[3].y = d_y;\r
3090 \r
3091         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3092         arrow[5].y = d_y - h;\r
3093 \r
3094         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3095         arrow[4].y = d_y - h;\r
3096 \r
3097         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3098         arrow[6].y = s_y;\r
3099     }\r
3100     else if( d_y == s_y ) {\r
3101         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3102 \r
3103         arrow[0].x = s_x;\r
3104         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3105 \r
3106         arrow[1].x = d_x - w;\r
3107         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3108 \r
3109         arrow[2].x = d_x - w;\r
3110         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3111 \r
3112         arrow[3].x = d_x;\r
3113         arrow[3].y = d_y;\r
3114 \r
3115         arrow[5].x = d_x - w;\r
3116         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3117 \r
3118         arrow[4].x = d_x - w;\r
3119         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3120 \r
3121         arrow[6].x = s_x;\r
3122         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3123     }\r
3124     else {\r
3125         /* [AS] Needed a lot of paper for this! :-) */\r
3126         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3127         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3128   \r
3129         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3130 \r
3131         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3132 \r
3133         x = s_x;\r
3134         y = s_y;\r
3135 \r
3136         arrow[0].x = Round(x - j);\r
3137         arrow[0].y = Round(y + j*dx);\r
3138 \r
3139         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3140         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3141 \r
3142         if( d_x > s_x ) {\r
3143             x = (double) d_x - k;\r
3144             y = (double) d_y - k*dy;\r
3145         }\r
3146         else {\r
3147             x = (double) d_x + k;\r
3148             y = (double) d_y + k*dy;\r
3149         }\r
3150 \r
3151         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3152 \r
3153         arrow[6].x = Round(x - j);\r
3154         arrow[6].y = Round(y + j*dx);\r
3155 \r
3156         arrow[2].x = Round(arrow[6].x + 2*j);\r
3157         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3158 \r
3159         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3160         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3161 \r
3162         arrow[4].x = d_x;\r
3163         arrow[4].y = d_y;\r
3164 \r
3165         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3166         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3167     }\r
3168 \r
3169     Polygon( hdc, arrow, 7 );\r
3170 }\r
3171 \r
3172 /* [AS] Draw an arrow between two squares */\r
3173 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3174 {\r
3175     int s_x, s_y, d_x, d_y;\r
3176     HPEN hpen;\r
3177     HPEN holdpen;\r
3178     HBRUSH hbrush;\r
3179     HBRUSH holdbrush;\r
3180     LOGBRUSH stLB;\r
3181 \r
3182     if( s_col == d_col && s_row == d_row ) {\r
3183         return;\r
3184     }\r
3185 \r
3186     /* Get source and destination points */\r
3187     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3188     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3189 \r
3190     if( d_y > s_y ) {\r
3191         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3192     }\r
3193     else if( d_y < s_y ) {\r
3194         d_y += squareSize / 2 + squareSize / 4;\r
3195     }\r
3196     else {\r
3197         d_y += squareSize / 2;\r
3198     }\r
3199 \r
3200     if( d_x > s_x ) {\r
3201         d_x += squareSize / 2 - squareSize / 4;\r
3202     }\r
3203     else if( d_x < s_x ) {\r
3204         d_x += squareSize / 2 + squareSize / 4;\r
3205     }\r
3206     else {\r
3207         d_x += squareSize / 2;\r
3208     }\r
3209 \r
3210     s_x += squareSize / 2;\r
3211     s_y += squareSize / 2;\r
3212 \r
3213     /* Adjust width */\r
3214     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3215 \r
3216     /* Draw */\r
3217     stLB.lbStyle = BS_SOLID;\r
3218     stLB.lbColor = appData.highlightArrowColor;\r
3219     stLB.lbHatch = 0;\r
3220 \r
3221     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3222     holdpen = SelectObject( hdc, hpen );\r
3223     hbrush = CreateBrushIndirect( &stLB );\r
3224     holdbrush = SelectObject( hdc, hbrush );\r
3225 \r
3226     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3227 \r
3228     SelectObject( hdc, holdpen );\r
3229     SelectObject( hdc, holdbrush );\r
3230     DeleteObject( hpen );\r
3231     DeleteObject( hbrush );\r
3232 }\r
3233 \r
3234 BOOL HasHighlightInfo()\r
3235 {\r
3236     BOOL result = FALSE;\r
3237 \r
3238     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3239         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3240     {\r
3241         result = TRUE;\r
3242     }\r
3243 \r
3244     return result;\r
3245 }\r
3246 \r
3247 BOOL IsDrawArrowEnabled()\r
3248 {\r
3249     BOOL result = FALSE;\r
3250 \r
3251     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3252         result = TRUE;\r
3253     }\r
3254 \r
3255     return result;\r
3256 }\r
3257 \r
3258 VOID DrawArrowHighlight( HDC hdc )\r
3259 {\r
3260     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3261         DrawArrowBetweenSquares( hdc,\r
3262             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3263             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3264     }\r
3265 }\r
3266 \r
3267 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3268 {\r
3269     HRGN result = NULL;\r
3270 \r
3271     if( HasHighlightInfo() ) {\r
3272         int x1, y1, x2, y2;\r
3273         int sx, sy, dx, dy;\r
3274 \r
3275         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3276         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3277 \r
3278         sx = MIN( x1, x2 );\r
3279         sy = MIN( y1, y2 );\r
3280         dx = MAX( x1, x2 ) + squareSize;\r
3281         dy = MAX( y1, y2 ) + squareSize;\r
3282 \r
3283         result = CreateRectRgn( sx, sy, dx, dy );\r
3284     }\r
3285 \r
3286     return result;\r
3287 }\r
3288 \r
3289 /*\r
3290     Warning: this function modifies the behavior of several other functions. \r
3291     \r
3292     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3293     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3294     repaint is scattered all over the place, which is not good for features such as\r
3295     "arrow highlighting" that require a full repaint of the board.\r
3296 \r
3297     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3298     user interaction, when speed is not so important) but especially to avoid errors\r
3299     in the displayed graphics.\r
3300 \r
3301     In such patched places, I always try refer to this function so there is a single\r
3302     place to maintain knowledge.\r
3303     \r
3304     To restore the original behavior, just return FALSE unconditionally.\r
3305 */\r
3306 BOOL IsFullRepaintPreferrable()\r
3307 {\r
3308     BOOL result = FALSE;\r
3309 \r
3310     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3311         /* Arrow may appear on the board */\r
3312         result = TRUE;\r
3313     }\r
3314 \r
3315     return result;\r
3316 }\r
3317 \r
3318 /* \r
3319     This function is called by DrawPosition to know whether a full repaint must\r
3320     be forced or not.\r
3321 \r
3322     Only DrawPosition may directly call this function, which makes use of \r
3323     some state information. Other function should call DrawPosition specifying \r
3324     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3325 */\r
3326 BOOL DrawPositionNeedsFullRepaint()\r
3327 {\r
3328     BOOL result = FALSE;\r
3329 \r
3330     /* \r
3331         Probably a slightly better policy would be to trigger a full repaint\r
3332         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3333         but animation is fast enough that it's difficult to notice.\r
3334     */\r
3335     if( animInfo.piece == EmptySquare ) {\r
3336         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3337             result = TRUE;\r
3338         }\r
3339     }\r
3340 \r
3341     return result;\r
3342 }\r
3343 \r
3344 static HBITMAP borderBitmap;\r
3345 \r
3346 VOID\r
3347 DrawBackgroundOnDC(HDC hdc)\r
3348 {\r
3349   \r
3350   BITMAP bi;\r
3351   HDC tmphdc;\r
3352   HBITMAP hbm;\r
3353   static char oldBorder[MSG_SIZ];\r
3354   int w = 600, h = 600;\r
3355 \r
3356   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3357     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3358     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3359   }\r
3360   if(borderBitmap == NULL) { // loading failed, use white\r
3361     FillRect( hdc, &boardRect, whitePieceBrush );\r
3362     return;\r
3363   }\r
3364   tmphdc = CreateCompatibleDC(hdc);\r
3365   hbm = SelectObject(tmphdc, borderBitmap);\r
3366   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3367             w = bi.bmWidth;\r
3368             h = bi.bmHeight;\r
3369   }\r
3370   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3371                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3372   SelectObject(tmphdc, hbm);\r
3373   DeleteDC(tmphdc);\r
3374 }\r
3375 \r
3376 VOID\r
3377 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3378 {\r
3379   int row, column, x, y, square_color, piece_color;\r
3380   ChessSquare piece;\r
3381   HBRUSH oldBrush;\r
3382   HDC texture_hdc = NULL;\r
3383 \r
3384   /* [AS] Initialize background textures if needed */\r
3385   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3386       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3387       if( backTextureSquareSize != squareSize \r
3388        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3389           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3390           backTextureSquareSize = squareSize;\r
3391           RebuildTextureSquareInfo();\r
3392       }\r
3393 \r
3394       texture_hdc = CreateCompatibleDC( hdc );\r
3395   }\r
3396 \r
3397   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3398     for (column = 0; column < BOARD_WIDTH; column++) {\r
3399   \r
3400       SquareToPos(row, column, &x, &y);\r
3401 \r
3402       piece = board[row][column];\r
3403 \r
3404       square_color = ((column + row) % 2) == 1;\r
3405       if( gameInfo.variant == VariantXiangqi ) {\r
3406           square_color = !InPalace(row, column);\r
3407           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3408           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3409       }\r
3410       piece_color = (int) piece < (int) BlackPawn;\r
3411 \r
3412 \r
3413       /* [HGM] holdings file: light square or black */\r
3414       if(column == BOARD_LEFT-2) {\r
3415             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3416                 square_color = 1;\r
3417             else {\r
3418                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3419                 continue;\r
3420             }\r
3421       } else\r
3422       if(column == BOARD_RGHT + 1 ) {\r
3423             if( row < gameInfo.holdingsSize )\r
3424                 square_color = 1;\r
3425             else {\r
3426                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3427                 continue;\r
3428             }\r
3429       }\r
3430       if(column == BOARD_LEFT-1 ) /* left align */\r
3431             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3432       else if( column == BOARD_RGHT) /* right align */\r
3433             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3434       else\r
3435       if (appData.monoMode) {\r
3436         if (piece == EmptySquare) {\r
3437           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3438                  square_color ? WHITENESS : BLACKNESS);\r
3439         } else {\r
3440           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3441         }\r
3442       } \r
3443       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3444           /* [AS] Draw the square using a texture bitmap */\r
3445           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3446           int r = row, c = column; // [HGM] do not flip board in flipView\r
3447           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3448 \r
3449           DrawTile( x, y, \r
3450               squareSize, squareSize, \r
3451               hdc, \r
3452               texture_hdc,\r
3453               backTextureSquareInfo[r][c].mode,\r
3454               backTextureSquareInfo[r][c].x,\r
3455               backTextureSquareInfo[r][c].y );\r
3456 \r
3457           SelectObject( texture_hdc, hbm );\r
3458 \r
3459           if (piece != EmptySquare) {\r
3460               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3461           }\r
3462       }\r
3463       else {\r
3464         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3465 \r
3466         oldBrush = SelectObject(hdc, brush );\r
3467         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3468         SelectObject(hdc, oldBrush);\r
3469         if (piece != EmptySquare)\r
3470           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3471       }\r
3472     }\r
3473   }\r
3474 \r
3475   if( texture_hdc != NULL ) {\r
3476     DeleteDC( texture_hdc );\r
3477   }\r
3478 }\r
3479 \r
3480 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3481 void fputDW(FILE *f, int x)\r
3482 {\r
3483         fputc(x     & 255, f);\r
3484         fputc(x>>8  & 255, f);\r
3485         fputc(x>>16 & 255, f);\r
3486         fputc(x>>24 & 255, f);\r
3487 }\r
3488 \r
3489 #define MAX_CLIPS 200   /* more than enough */\r
3490 \r
3491 VOID\r
3492 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3493 {\r
3494 //  HBITMAP bufferBitmap;\r
3495   BITMAP bi;\r
3496 //  RECT Rect;\r
3497   HDC tmphdc;\r
3498   HBITMAP hbm;\r
3499   int w = 100, h = 50;\r
3500 \r
3501   if(logo == NULL) {\r
3502     if(!logoHeight) return;\r
3503     FillRect( hdc, &logoRect, whitePieceBrush );\r
3504   }\r
3505 //  GetClientRect(hwndMain, &Rect);\r
3506 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3507 //                                      Rect.bottom-Rect.top+1);\r
3508   tmphdc = CreateCompatibleDC(hdc);\r
3509   hbm = SelectObject(tmphdc, logo);\r
3510   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3511             w = bi.bmWidth;\r
3512             h = bi.bmHeight;\r
3513   }\r
3514   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3515                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3516   SelectObject(tmphdc, hbm);\r
3517   DeleteDC(tmphdc);\r
3518 }\r
3519 \r
3520 VOID\r
3521 DisplayLogos()\r
3522 {\r
3523   if(logoHeight) {\r
3524         HDC hdc = GetDC(hwndMain);\r
3525         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3526         if(appData.autoLogo) {\r
3527           \r
3528           switch(gameMode) { // pick logos based on game mode\r
3529             case IcsObserving:\r
3530                 whiteLogo = second.programLogo; // ICS logo\r
3531                 blackLogo = second.programLogo;\r
3532             default:\r
3533                 break;\r
3534             case IcsPlayingWhite:\r
3535                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3536                 blackLogo = second.programLogo; // ICS logo\r
3537                 break;\r
3538             case IcsPlayingBlack:\r
3539                 whiteLogo = second.programLogo; // ICS logo\r
3540                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3541                 break;\r
3542             case TwoMachinesPlay:\r
3543                 if(first.twoMachinesColor[0] == 'b') {\r
3544                     whiteLogo = second.programLogo;\r
3545                     blackLogo = first.programLogo;\r
3546                 }\r
3547                 break;\r
3548             case MachinePlaysWhite:\r
3549                 blackLogo = userLogo;\r
3550                 break;\r
3551             case MachinePlaysBlack:\r
3552                 whiteLogo = userLogo;\r
3553                 blackLogo = first.programLogo;\r
3554           }\r
3555         }\r
3556         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3557         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3558         ReleaseDC(hwndMain, hdc);\r
3559   }\r
3560 }\r
3561 \r
3562 void\r
3563 UpdateLogos(int display)\r
3564 { // called after loading new engine(s), in tourney or from menu\r
3565   LoadLogo(&first, 0, FALSE);\r
3566   LoadLogo(&second, 1, appData.icsActive);\r
3567   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3568   if(display) DisplayLogos();\r
3569 }\r
3570 \r
3571 static HDC hdcSeek;\r
3572 \r
3573 // [HGM] seekgraph\r
3574 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3575 {\r
3576     POINT stPt;\r
3577     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3578     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3579     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3580     SelectObject( hdcSeek, hp );\r
3581 }\r
3582 \r
3583 // front-end wrapper for drawing functions to do rectangles\r
3584 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3585 {\r
3586     HPEN hp;\r
3587     RECT rc;\r
3588 \r
3589     if (hdcSeek == NULL) {\r
3590     hdcSeek = GetDC(hwndMain);\r
3591       if (!appData.monoMode) {\r
3592         SelectPalette(hdcSeek, hPal, FALSE);\r
3593         RealizePalette(hdcSeek);\r
3594       }\r
3595     }\r
3596     hp = SelectObject( hdcSeek, gridPen );\r
3597     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3598     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3599     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3600     SelectObject( hdcSeek, hp );\r
3601 }\r
3602 \r
3603 // front-end wrapper for putting text in graph\r
3604 void DrawSeekText(char *buf, int x, int y)\r
3605 {\r
3606         SIZE stSize;\r
3607         SetBkMode( hdcSeek, TRANSPARENT );\r
3608         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3609         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3610 }\r
3611 \r
3612 void DrawSeekDot(int x, int y, int color)\r
3613 {\r
3614         int square = color & 0x80;\r
3615         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3616                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3617         color &= 0x7F;\r
3618         if(square)\r
3619             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3620                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3621         else\r
3622             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3623                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3624             SelectObject(hdcSeek, oldBrush);\r
3625 }\r
3626 \r
3627 void DrawSeekOpen()\r
3628 {\r
3629 }\r
3630 \r
3631 void DrawSeekClose()\r
3632 {\r
3633 }\r
3634 \r
3635 VOID\r
3636 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3637 {\r
3638   static Board lastReq[2], lastDrawn[2];\r
3639   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3640   static int lastDrawnFlipView = 0;\r
3641   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3642   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3643   HDC tmphdc;\r
3644   HDC hdcmem;\r
3645   HBITMAP bufferBitmap;\r
3646   HBITMAP oldBitmap;\r
3647   RECT Rect;\r
3648   HRGN clips[MAX_CLIPS];\r
3649   ChessSquare dragged_piece = EmptySquare;\r
3650   int nr = twoBoards*partnerUp;\r
3651 \r
3652   /* I'm undecided on this - this function figures out whether a full\r
3653    * repaint is necessary on its own, so there's no real reason to have the\r
3654    * caller tell it that.  I think this can safely be set to FALSE - but\r
3655    * if we trust the callers not to request full repaints unnessesarily, then\r
3656    * we could skip some clipping work.  In other words, only request a full\r
3657    * redraw when the majority of pieces have changed positions (ie. flip, \r
3658    * gamestart and similar)  --Hawk\r
3659    */\r
3660   Boolean fullrepaint = repaint;\r
3661 \r
3662   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3663 \r
3664   if( DrawPositionNeedsFullRepaint() ) {\r
3665       fullrepaint = TRUE;\r
3666   }\r
3667 \r
3668   if (board == NULL) {\r
3669     if (!lastReqValid[nr]) {\r
3670       return;\r
3671     }\r
3672     board = lastReq[nr];\r
3673   } else {\r
3674     CopyBoard(lastReq[nr], board);\r
3675     lastReqValid[nr] = 1;\r
3676   }\r
3677 \r
3678   if (doingSizing) {\r
3679     return;\r
3680   }\r
3681 \r
3682   if (IsIconic(hwndMain)) {\r
3683     return;\r
3684   }\r
3685 \r
3686   if (hdc == NULL) {\r
3687     hdc = GetDC(hwndMain);\r
3688     if (!appData.monoMode) {\r
3689       SelectPalette(hdc, hPal, FALSE);\r
3690       RealizePalette(hdc);\r
3691     }\r
3692     releaseDC = TRUE;\r
3693   } else {\r
3694     releaseDC = FALSE;\r
3695   }\r
3696 \r
3697   /* Create some work-DCs */\r
3698   hdcmem = CreateCompatibleDC(hdc);\r
3699   tmphdc = CreateCompatibleDC(hdc);\r
3700 \r
3701   /* If dragging is in progress, we temporarely remove the piece */\r
3702   /* [HGM] or temporarily decrease count if stacked              */\r
3703   /*       !! Moved to before board compare !!                   */\r
3704   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3705     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3706     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3707             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3708         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3709     } else \r
3710     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3711             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3712         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3713     } else \r
3714         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3715   }\r
3716 \r
3717   /* Figure out which squares need updating by comparing the \r
3718    * newest board with the last drawn board and checking if\r
3719    * flipping has changed.\r
3720    */\r
3721   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3722     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3723       for (column = 0; column < BOARD_WIDTH; column++) {\r
3724         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3725           SquareToPos(row, column, &x, &y);\r
3726           clips[num_clips++] =\r
3727             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3728         }\r
3729       }\r
3730     }\r
3731    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3732     for (i=0; i<2; i++) {\r
3733       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3734           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3735         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3736             lastDrawnHighlight.sq[i].y >= 0) {\r
3737           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3738                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3739           clips[num_clips++] =\r
3740             CreateRectRgn(x - lineGap, y - lineGap, \r
3741                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3742         }\r
3743         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3744           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3745           clips[num_clips++] =\r
3746             CreateRectRgn(x - lineGap, y - lineGap, \r
3747                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3748         }\r
3749       }\r
3750     }\r
3751     for (i=0; i<2; i++) {\r
3752       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3753           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3754         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3755             lastDrawnPremove.sq[i].y >= 0) {\r
3756           SquareToPos(lastDrawnPremove.sq[i].y,\r
3757                       lastDrawnPremove.sq[i].x, &x, &y);\r
3758           clips[num_clips++] =\r
3759             CreateRectRgn(x - lineGap, y - lineGap, \r
3760                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3761         }\r
3762         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3763             premoveHighlightInfo.sq[i].y >= 0) {\r
3764           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3765                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3766           clips[num_clips++] =\r
3767             CreateRectRgn(x - lineGap, y - lineGap, \r
3768                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3769         }\r
3770       }\r
3771     }\r
3772    } else { // nr == 1\r
3773         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3774         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3775         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3776         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3777       for (i=0; i<2; i++) {\r
3778         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3779             partnerHighlightInfo.sq[i].y >= 0) {\r
3780           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3781                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3782           clips[num_clips++] =\r
3783             CreateRectRgn(x - lineGap, y - lineGap, \r
3784                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3785         }\r
3786         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3787             oldPartnerHighlight.sq[i].y >= 0) {\r
3788           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3789                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3790           clips[num_clips++] =\r
3791             CreateRectRgn(x - lineGap, y - lineGap, \r
3792                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3793         }\r
3794       }\r
3795    }\r
3796   } else {\r
3797     fullrepaint = TRUE;\r
3798   }\r
3799 \r
3800   /* Create a buffer bitmap - this is the actual bitmap\r
3801    * being written to.  When all the work is done, we can\r
3802    * copy it to the real DC (the screen).  This avoids\r
3803    * the problems with flickering.\r
3804    */\r
3805   GetClientRect(hwndMain, &Rect);\r
3806   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3807                                         Rect.bottom-Rect.top+1);\r
3808   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3809   if (!appData.monoMode) {\r
3810     SelectPalette(hdcmem, hPal, FALSE);\r
3811   }\r
3812 \r
3813   /* Create clips for dragging */\r
3814   if (!fullrepaint) {\r
3815     if (dragInfo.from.x >= 0) {\r
3816       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3817       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3818     }\r
3819     if (dragInfo.start.x >= 0) {\r
3820       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3821       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3822     }\r
3823     if (dragInfo.pos.x >= 0) {\r
3824       x = dragInfo.pos.x - squareSize / 2;\r
3825       y = dragInfo.pos.y - squareSize / 2;\r
3826       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3827     }\r
3828     if (dragInfo.lastpos.x >= 0) {\r
3829       x = dragInfo.lastpos.x - squareSize / 2;\r
3830       y = dragInfo.lastpos.y - squareSize / 2;\r
3831       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3832     }\r
3833   }\r
3834 \r
3835   /* Are we animating a move?  \r
3836    * If so, \r
3837    *   - remove the piece from the board (temporarely)\r
3838    *   - calculate the clipping region\r
3839    */\r
3840   if (!fullrepaint) {\r
3841     if (animInfo.piece != EmptySquare) {\r
3842       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3843       x = boardRect.left + animInfo.lastpos.x;\r
3844       y = boardRect.top + animInfo.lastpos.y;\r
3845       x2 = boardRect.left + animInfo.pos.x;\r
3846       y2 = boardRect.top + animInfo.pos.y;\r
3847       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3848       /* Slight kludge.  The real problem is that after AnimateMove is\r
3849          done, the position on the screen does not match lastDrawn.\r
3850          This currently causes trouble only on e.p. captures in\r
3851          atomic, where the piece moves to an empty square and then\r
3852          explodes.  The old and new positions both had an empty square\r
3853          at the destination, but animation has drawn a piece there and\r
3854          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3855       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3856     }\r
3857   }\r
3858 \r
3859   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3860   if (num_clips == 0)\r
3861     fullrepaint = TRUE;\r
3862 \r
3863   /* Set clipping on the memory DC */\r
3864   if (!fullrepaint) {\r
3865     SelectClipRgn(hdcmem, clips[0]);\r
3866     for (x = 1; x < num_clips; x++) {\r
3867       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3868         abort();  // this should never ever happen!\r
3869     }\r
3870   }\r
3871 \r
3872   /* Do all the drawing to the memory DC */\r
3873   if(explodeInfo.radius) { // [HGM] atomic\r
3874         HBRUSH oldBrush;\r
3875         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3876         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3877         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3878         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3879         x += squareSize/2;\r
3880         y += squareSize/2;\r
3881         if(!fullrepaint) {\r
3882           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3883           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3884         }\r
3885         DrawGridOnDC(hdcmem);\r
3886         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3887         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3888         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3889         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3890         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3891         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3892         SelectObject(hdcmem, oldBrush);\r
3893   } else {\r
3894     if(border) DrawBackgroundOnDC(hdcmem);\r
3895     DrawGridOnDC(hdcmem);\r
3896     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3897         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3898         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3899     } else {\r
3900         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3901         oldPartnerHighlight = partnerHighlightInfo;\r
3902     }\r
3903     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3904   }\r
3905   if(nr == 0) // [HGM] dual: markers only on left board\r
3906   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3907     for (column = 0; column < BOARD_WIDTH; column++) {\r
3908         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3909             HBRUSH oldBrush = SelectObject(hdcmem, \r
3910                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3911             SquareToPos(row, column, &x, &y);\r
3912             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3913                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3914             SelectObject(hdcmem, oldBrush);\r
3915         }\r
3916     }\r
3917   }\r
3918 \r
3919   if( appData.highlightMoveWithArrow ) {\r
3920     DrawArrowHighlight(hdcmem);\r
3921   }\r
3922 \r
3923   DrawCoordsOnDC(hdcmem);\r
3924 \r
3925   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3926                  /* to make sure lastDrawn contains what is actually drawn */\r
3927 \r
3928   /* Put the dragged piece back into place and draw it (out of place!) */\r
3929     if (dragged_piece != EmptySquare) {\r
3930     /* [HGM] or restack */\r
3931     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3932                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3933     else\r
3934     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3935                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3936     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3937     x = dragInfo.pos.x - squareSize / 2;\r
3938     y = dragInfo.pos.y - squareSize / 2;\r
3939     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3940                   ((int) dragInfo.piece < (int) BlackPawn), \r
3941                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3942   }   \r
3943   \r
3944   /* Put the animated piece back into place and draw it */\r
3945   if (animInfo.piece != EmptySquare) {\r
3946     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3947     x = boardRect.left + animInfo.pos.x;\r
3948     y = boardRect.top + animInfo.pos.y;\r
3949     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3950                   ((int) animInfo.piece < (int) BlackPawn),\r
3951                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3952   }\r
3953 \r
3954   /* Release the bufferBitmap by selecting in the old bitmap \r
3955    * and delete the memory DC\r
3956    */\r
3957   SelectObject(hdcmem, oldBitmap);\r
3958   DeleteDC(hdcmem);\r
3959 \r
3960   /* Set clipping on the target DC */\r
3961   if (!fullrepaint) {\r
3962     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3963         RECT rect;\r
3964         GetRgnBox(clips[x], &rect);\r
3965         DeleteObject(clips[x]);\r
3966         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3967                           rect.right + wpMain.width/2, rect.bottom);\r
3968     }\r
3969     SelectClipRgn(hdc, clips[0]);\r
3970     for (x = 1; x < num_clips; x++) {\r
3971       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3972         abort();   // this should never ever happen!\r
3973     } \r
3974   }\r
3975 \r
3976   /* Copy the new bitmap onto the screen in one go.\r
3977    * This way we avoid any flickering\r
3978    */\r
3979   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3980   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3981          boardRect.right - boardRect.left,\r
3982          boardRect.bottom - boardRect.top,\r
3983          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3984   if(saveDiagFlag) { \r
3985     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3986     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3987 \r
3988     GetObject(bufferBitmap, sizeof(b), &b);\r
3989     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3990         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3991         bih.biWidth = b.bmWidth;\r
3992         bih.biHeight = b.bmHeight;\r
3993         bih.biPlanes = 1;\r
3994         bih.biBitCount = b.bmBitsPixel;\r
3995         bih.biCompression = 0;\r
3996         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3997         bih.biXPelsPerMeter = 0;\r
3998         bih.biYPelsPerMeter = 0;\r
3999         bih.biClrUsed = 0;\r
4000         bih.biClrImportant = 0;\r
4001 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4002 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4003         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4004 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4005 \r
4006         wb = b.bmWidthBytes;\r
4007         // count colors\r
4008         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4009                 int k = ((int*) pData)[i];\r
4010                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4011                 if(j >= 16) break;\r
4012                 color[j] = k;\r
4013                 if(j >= nrColors) nrColors = j+1;\r
4014         }\r
4015         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4016                 INT p = 0;\r
4017                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4018                     for(w=0; w<(wb>>2); w+=2) {\r
4019                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4020                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4021                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4022                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4023                         pData[p++] = m | j<<4;\r
4024                     }\r
4025                     while(p&3) pData[p++] = 0;\r
4026                 }\r
4027                 fac = 3;\r
4028                 wb = ((wb+31)>>5)<<2;\r
4029         }\r
4030         // write BITMAPFILEHEADER\r
4031         fprintf(diagFile, "BM");\r
4032         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4033         fputDW(diagFile, 0);\r
4034         fputDW(diagFile, 0x36 + (fac?64:0));\r
4035         // write BITMAPINFOHEADER\r
4036         fputDW(diagFile, 40);\r
4037         fputDW(diagFile, b.bmWidth);\r
4038         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4039         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4040         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4041         fputDW(diagFile, 0);\r
4042         fputDW(diagFile, 0);\r
4043         fputDW(diagFile, 0);\r
4044         fputDW(diagFile, 0);\r
4045         fputDW(diagFile, 0);\r
4046         fputDW(diagFile, 0);\r
4047         // write color table\r
4048         if(fac)\r
4049         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4050         // write bitmap data\r
4051         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4052                 fputc(pData[i], diagFile);\r
4053         free(pData);\r
4054      }\r
4055   }\r
4056 \r
4057   SelectObject(tmphdc, oldBitmap);\r
4058 \r
4059   /* Massive cleanup */\r
4060   for (x = 0; x < num_clips; x++)\r
4061     DeleteObject(clips[x]);\r
4062 \r
4063   DeleteDC(tmphdc);\r
4064   DeleteObject(bufferBitmap);\r
4065 \r
4066   if (releaseDC) \r
4067     ReleaseDC(hwndMain, hdc);\r
4068   \r
4069   if (lastDrawnFlipView != flipView && nr == 0) {\r
4070     if (flipView)\r
4071       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4072     else\r
4073       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4074   }\r
4075 \r
4076 /*  CopyBoard(lastDrawn, board);*/\r
4077   lastDrawnHighlight = highlightInfo;\r
4078   lastDrawnPremove   = premoveHighlightInfo;\r
4079   lastDrawnFlipView = flipView;\r
4080   lastDrawnValid[nr] = 1;\r
4081 }\r
4082 \r
4083 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4084 int\r
4085 SaveDiagram(f)\r
4086      FILE *f;\r
4087 {\r
4088     saveDiagFlag = 1; diagFile = f;\r
4089     HDCDrawPosition(NULL, TRUE, NULL);\r
4090     saveDiagFlag = 0;\r
4091 \r
4092     fclose(f);\r
4093     return TRUE;\r
4094 }\r
4095 \r
4096 \r
4097 /*---------------------------------------------------------------------------*\\r
4098 | CLIENT PAINT PROCEDURE\r
4099 |   This is the main event-handler for the WM_PAINT message.\r
4100 |\r
4101 \*---------------------------------------------------------------------------*/\r
4102 VOID\r
4103 PaintProc(HWND hwnd)\r
4104 {\r
4105   HDC         hdc;\r
4106   PAINTSTRUCT ps;\r
4107   HFONT       oldFont;\r
4108 \r
4109   if((hdc = BeginPaint(hwnd, &ps))) {\r
4110     if (IsIconic(hwnd)) {\r
4111       DrawIcon(hdc, 2, 2, iconCurrent);\r
4112     } else {\r
4113       if (!appData.monoMode) {\r
4114         SelectPalette(hdc, hPal, FALSE);\r
4115         RealizePalette(hdc);\r
4116       }\r
4117       HDCDrawPosition(hdc, 1, NULL);\r
4118       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4119         flipView = !flipView; partnerUp = !partnerUp;\r
4120         HDCDrawPosition(hdc, 1, NULL);\r
4121         flipView = !flipView; partnerUp = !partnerUp;\r
4122       }\r
4123       oldFont =\r
4124         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4125       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4126                  ETO_CLIPPED|ETO_OPAQUE,\r
4127                  &messageRect, messageText, strlen(messageText), NULL);\r
4128       SelectObject(hdc, oldFont);\r
4129       DisplayBothClocks();\r
4130       DisplayLogos();\r
4131     }\r
4132     EndPaint(hwnd,&ps);\r
4133   }\r
4134 \r
4135   return;\r
4136 }\r
4137 \r
4138 \r
4139 /*\r
4140  * If the user selects on a border boundary, return -1; if off the board,\r
4141  *   return -2.  Otherwise map the event coordinate to the square.\r
4142  * The offset boardRect.left or boardRect.top must already have been\r
4143  *   subtracted from x.\r
4144  */\r
4145 int EventToSquare(x, limit)\r
4146      int x, limit;\r
4147 {\r
4148   if (x <= border)\r
4149     return -2;\r
4150   if (x < lineGap + border)\r
4151     return -1;\r
4152   x -= lineGap + border;\r
4153   if ((x % (squareSize + lineGap)) >= squareSize)\r
4154     return -1;\r
4155   x /= (squareSize + lineGap);\r
4156     if (x >= limit)\r
4157     return -2;\r
4158   return x;\r
4159 }\r
4160 \r
4161 typedef struct {\r
4162   char piece;\r
4163   int command;\r
4164   char* name;\r
4165 } DropEnable;\r
4166 \r
4167 DropEnable dropEnables[] = {\r
4168   { 'P', DP_Pawn, N_("Pawn") },\r
4169   { 'N', DP_Knight, N_("Knight") },\r
4170   { 'B', DP_Bishop, N_("Bishop") },\r
4171   { 'R', DP_Rook, N_("Rook") },\r
4172   { 'Q', DP_Queen, N_("Queen") },\r
4173 };\r
4174 \r
4175 VOID\r
4176 SetupDropMenu(HMENU hmenu)\r
4177 {\r
4178   int i, count, enable;\r
4179   char *p;\r
4180   extern char white_holding[], black_holding[];\r
4181   char item[MSG_SIZ];\r
4182 \r
4183   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4184     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4185                dropEnables[i].piece);\r
4186     count = 0;\r
4187     while (p && *p++ == dropEnables[i].piece) count++;\r
4188       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4189     enable = count > 0 || !appData.testLegality\r
4190       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4191                       && !appData.icsActive);\r
4192     ModifyMenu(hmenu, dropEnables[i].command,\r
4193                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4194                dropEnables[i].command, item);\r
4195   }\r
4196 }\r
4197 \r
4198 void DragPieceBegin(int x, int y, Boolean instantly)\r
4199 {\r
4200       dragInfo.lastpos.x = boardRect.left + x;\r
4201       dragInfo.lastpos.y = boardRect.top + y;\r
4202       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4203       dragInfo.from.x = fromX;\r
4204       dragInfo.from.y = fromY;\r
4205       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4206       dragInfo.start = dragInfo.from;\r
4207       SetCapture(hwndMain);\r
4208 }\r
4209 \r
4210 void DragPieceEnd(int x, int y)\r
4211 {\r
4212     ReleaseCapture();\r
4213     dragInfo.start.x = dragInfo.start.y = -1;\r
4214     dragInfo.from = dragInfo.start;\r
4215     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4216 }\r
4217 \r
4218 void ChangeDragPiece(ChessSquare piece)\r
4219 {\r
4220     dragInfo.piece = piece;\r
4221 }\r
4222 \r
4223 /* Event handler for mouse messages */\r
4224 VOID\r
4225 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4226 {\r
4227   int x, y, menuNr;\r
4228   POINT pt;\r
4229   static int recursive = 0;\r
4230   HMENU hmenu;\r
4231   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4232 \r
4233   if (recursive) {\r
4234     if (message == WM_MBUTTONUP) {\r
4235       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4236          to the middle button: we simulate pressing the left button too!\r
4237          */\r
4238       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4239       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4240     }\r
4241     return;\r
4242   }\r
4243   recursive++;\r
4244   \r
4245   pt.x = LOWORD(lParam);\r
4246   pt.y = HIWORD(lParam);\r
4247   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4248   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4249   if (!flipView && y >= 0) {\r
4250     y = BOARD_HEIGHT - 1 - y;\r
4251   }\r
4252   if (flipView && x >= 0) {\r
4253     x = BOARD_WIDTH - 1 - x;\r
4254   }\r
4255 \r
4256   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4257 \r
4258   switch (message) {\r
4259   case WM_LBUTTONDOWN:\r
4260       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4261         ClockClick(flipClock); break;\r
4262       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4263         ClockClick(!flipClock); break;\r
4264       }\r
4265       dragInfo.start.x = dragInfo.start.y = -1;\r
4266       dragInfo.from = dragInfo.start;\r
4267     if(fromX == -1 && frozen) { // not sure where this is for\r
4268                 fromX = fromY = -1; \r
4269       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4270       break;\r
4271     }\r
4272       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4273       DrawPosition(TRUE, NULL);\r
4274     break;\r
4275 \r
4276   case WM_LBUTTONUP:\r
4277       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4278       DrawPosition(TRUE, NULL);\r
4279     break;\r
4280 \r
4281   case WM_MOUSEMOVE:\r
4282     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4283     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4284     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4285     if ((appData.animateDragging || appData.highlightDragging)\r
4286         && (wParam & MK_LBUTTON)\r
4287         && dragInfo.from.x >= 0) \r
4288     {\r
4289       BOOL full_repaint = FALSE;\r
4290 \r
4291       if (appData.animateDragging) {\r
4292         dragInfo.pos = pt;\r
4293       }\r
4294       if (appData.highlightDragging) {\r
4295         SetHighlights(fromX, fromY, x, y);\r
4296         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4297             full_repaint = TRUE;\r
4298         }\r
4299       }\r
4300       \r
4301       DrawPosition( full_repaint, NULL);\r
4302       \r
4303       dragInfo.lastpos = dragInfo.pos;\r
4304     }\r
4305     break;\r
4306 \r
4307   case WM_MOUSEWHEEL: // [DM]\r
4308     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4309        /* Mouse Wheel is being rolled forward\r
4310         * Play moves forward\r
4311         */\r
4312        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4313                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4314        /* Mouse Wheel is being rolled backward\r
4315         * Play moves backward\r
4316         */\r
4317        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4318                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4319     }\r
4320     break;\r
4321 \r
4322   case WM_MBUTTONUP:\r
4323   case WM_RBUTTONUP:\r
4324     ReleaseCapture();\r
4325     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4326     break;\r
4327  \r
4328   case WM_MBUTTONDOWN:\r
4329   case WM_RBUTTONDOWN:\r
4330     ErrorPopDown();\r
4331     ReleaseCapture();\r
4332     fromX = fromY = -1;\r
4333     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4334     dragInfo.start.x = dragInfo.start.y = -1;\r
4335     dragInfo.from = dragInfo.start;\r
4336     dragInfo.lastpos = dragInfo.pos;\r
4337     if (appData.highlightDragging) {\r
4338       ClearHighlights();\r
4339     }\r
4340     if(y == -2) {\r
4341       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4342       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4343           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4344       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4345           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4346       }\r
4347       break;\r
4348     }\r
4349     DrawPosition(TRUE, NULL);\r
4350 \r
4351     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4352     switch (menuNr) {\r
4353     case 0:\r
4354       if (message == WM_MBUTTONDOWN) {\r
4355         buttonCount = 3;  /* even if system didn't think so */\r
4356         if (wParam & MK_SHIFT) \r
4357           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4358         else\r
4359           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4360       } else { /* message == WM_RBUTTONDOWN */\r
4361         /* Just have one menu, on the right button.  Windows users don't\r
4362            think to try the middle one, and sometimes other software steals\r
4363            it, or it doesn't really exist. */\r
4364         if(gameInfo.variant != VariantShogi)\r
4365             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4366         else\r
4367             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4368       }\r
4369       break;\r
4370     case 2:\r
4371       SetCapture(hwndMain);\r
4372       break;\r
4373     case 1:\r
4374       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4375       SetupDropMenu(hmenu);\r
4376       MenuPopup(hwnd, pt, hmenu, -1);\r
4377     default:\r
4378       break;\r
4379     }\r
4380     break;\r
4381   }\r
4382 \r
4383   recursive--;\r
4384 }\r
4385 \r
4386 /* Preprocess messages for buttons in main window */\r
4387 LRESULT CALLBACK\r
4388 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4389 {\r
4390   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4391   int i, dir;\r
4392 \r
4393   for (i=0; i<N_BUTTONS; i++) {\r
4394     if (buttonDesc[i].id == id) break;\r
4395   }\r
4396   if (i == N_BUTTONS) return 0;\r
4397   switch (message) {\r
4398   case WM_KEYDOWN:\r
4399     switch (wParam) {\r
4400     case VK_LEFT:\r
4401     case VK_RIGHT:\r
4402       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4403       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4404       return TRUE;\r
4405     }\r
4406     break;\r
4407   case WM_CHAR:\r
4408     switch (wParam) {\r
4409     case '\r':\r
4410       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4411       return TRUE;\r
4412     default:\r
4413       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4414         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4415         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4416         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4417         SetFocus(h);\r
4418         SendMessage(h, WM_CHAR, wParam, lParam);\r
4419         return TRUE;\r
4420       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4421         TypeInEvent((char)wParam);\r
4422       }\r
4423       break;\r
4424     }\r
4425     break;\r
4426   }\r
4427   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4428 }\r
4429 \r
4430 /* Process messages for Promotion dialog box */\r
4431 LRESULT CALLBACK\r
4432 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4433 {\r
4434   char promoChar;\r
4435 \r
4436   switch (message) {\r
4437   case WM_INITDIALOG: /* message: initialize dialog box */\r
4438     /* Center the dialog over the application window */\r
4439     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4440     Translate(hDlg, DLG_PromotionKing);\r
4441     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4442       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4443        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4444        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4445                SW_SHOW : SW_HIDE);\r
4446     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4447     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4448        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4449          PieceToChar(WhiteAngel) != '~') ||\r
4450         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4451          PieceToChar(BlackAngel) != '~')   ) ?\r
4452                SW_SHOW : SW_HIDE);\r
4453     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4454        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4455          PieceToChar(WhiteMarshall) != '~') ||\r
4456         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4457          PieceToChar(BlackMarshall) != '~')   ) ?\r
4458                SW_SHOW : SW_HIDE);\r
4459     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4460     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4461        gameInfo.variant != VariantShogi ?\r
4462                SW_SHOW : SW_HIDE);\r
4463     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4464        gameInfo.variant != VariantShogi ?\r
4465                SW_SHOW : SW_HIDE);\r
4466     if(gameInfo.variant == VariantShogi) {\r
4467         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4468         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4469         SetWindowText(hDlg, "Promote?");\r
4470     }\r
4471     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4472        gameInfo.variant == VariantSuper ?\r
4473                SW_SHOW : SW_HIDE);\r
4474     return TRUE;\r
4475 \r
4476   case WM_COMMAND: /* message: received a command */\r
4477     switch (LOWORD(wParam)) {\r
4478     case IDCANCEL:\r
4479       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4480       ClearHighlights();\r
4481       DrawPosition(FALSE, NULL);\r
4482       return TRUE;\r
4483     case PB_King:\r
4484       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4485       break;\r
4486     case PB_Queen:\r
4487       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4488       break;\r
4489     case PB_Rook:\r
4490       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4491       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4492       break;\r
4493     case PB_Bishop:\r
4494       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4495       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4496       break;\r
4497     case PB_Chancellor:\r
4498       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4499       break;\r
4500     case PB_Archbishop:\r
4501       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4502       break;\r
4503     case PB_Knight:\r
4504       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4505       break;\r
4506     default:\r
4507       return FALSE;\r
4508     }\r
4509     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4510     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4511     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4512     fromX = fromY = -1;\r
4513     if (!appData.highlightLastMove) {\r
4514       ClearHighlights();\r
4515       DrawPosition(FALSE, NULL);\r
4516     }\r
4517     return TRUE;\r
4518   }\r
4519   return FALSE;\r
4520 }\r
4521 \r
4522 /* Pop up promotion dialog */\r
4523 VOID\r
4524 PromotionPopup(HWND hwnd)\r
4525 {\r
4526   FARPROC lpProc;\r
4527 \r
4528   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4529   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4530     hwnd, (DLGPROC)lpProc);\r
4531   FreeProcInstance(lpProc);\r
4532 }\r
4533 \r
4534 void\r
4535 PromotionPopUp()\r
4536 {\r
4537   DrawPosition(TRUE, NULL);\r
4538   PromotionPopup(hwndMain);\r
4539 }\r
4540 \r
4541 VOID\r
4542 LoadGameDialog(HWND hwnd, char* title)\r
4543 {\r
4544   UINT number = 0;\r
4545   FILE *f;\r
4546   char fileTitle[MSG_SIZ];\r
4547   f = OpenFileDialog(hwnd, "rb", "",\r
4548                      appData.oldSaveStyle ? "gam" : "pgn",\r
4549                      GAME_FILT,\r
4550                      title, &number, fileTitle, NULL);\r
4551   if (f != NULL) {\r
4552     cmailMsgLoaded = FALSE;\r
4553     if (number == 0) {\r
4554       int error = GameListBuild(f);\r
4555       if (error) {\r
4556         DisplayError(_("Cannot build game list"), error);\r
4557       } else if (!ListEmpty(&gameList) &&\r
4558                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4559         GameListPopUp(f, fileTitle);\r
4560         return;\r
4561       }\r
4562       GameListDestroy();\r
4563       number = 1;\r
4564     }\r
4565     LoadGame(f, number, fileTitle, FALSE);\r
4566   }\r
4567 }\r
4568 \r
4569 int get_term_width()\r
4570 {\r
4571     HDC hdc;\r
4572     TEXTMETRIC tm;\r
4573     RECT rc;\r
4574     HFONT hfont, hold_font;\r
4575     LOGFONT lf;\r
4576     HWND hText;\r
4577 \r
4578     if (hwndConsole)\r
4579         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4580     else\r
4581         return 79;\r
4582 \r
4583     // get the text metrics\r
4584     hdc = GetDC(hText);\r
4585     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4586     if (consoleCF.dwEffects & CFE_BOLD)\r
4587         lf.lfWeight = FW_BOLD;\r
4588     if (consoleCF.dwEffects & CFE_ITALIC)\r
4589         lf.lfItalic = TRUE;\r
4590     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4591         lf.lfStrikeOut = TRUE;\r
4592     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4593         lf.lfUnderline = TRUE;\r
4594     hfont = CreateFontIndirect(&lf);\r
4595     hold_font = SelectObject(hdc, hfont);\r
4596     GetTextMetrics(hdc, &tm);\r
4597     SelectObject(hdc, hold_font);\r
4598     DeleteObject(hfont);\r
4599     ReleaseDC(hText, hdc);\r
4600 \r
4601     // get the rectangle\r
4602     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4603 \r
4604     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4605 }\r
4606 \r
4607 void UpdateICSWidth(HWND hText)\r
4608 {\r
4609     LONG old_width, new_width;\r
4610 \r
4611     new_width = get_term_width(hText, FALSE);\r
4612     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4613     if (new_width != old_width)\r
4614     {\r
4615         ics_update_width(new_width);\r
4616         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4617     }\r
4618 }\r
4619 \r
4620 VOID\r
4621 ChangedConsoleFont()\r
4622 {\r
4623   CHARFORMAT cfmt;\r
4624   CHARRANGE tmpsel, sel;\r
4625   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4626   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4627   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4628   PARAFORMAT paraf;\r
4629 \r
4630   cfmt.cbSize = sizeof(CHARFORMAT);\r
4631   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4632     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4633                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4634   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4635    * size.  This was undocumented in the version of MSVC++ that I had\r
4636    * when I wrote the code, but is apparently documented now.\r
4637    */\r
4638   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4639   cfmt.bCharSet = f->lf.lfCharSet;\r
4640   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4641   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4642   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4643   /* Why are the following seemingly needed too? */\r
4644   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4645   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4646   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4647   tmpsel.cpMin = 0;\r
4648   tmpsel.cpMax = -1; /*999999?*/\r
4649   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4650   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4651   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4652    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4653    */\r
4654   paraf.cbSize = sizeof(paraf);\r
4655   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4656   paraf.dxStartIndent = 0;\r
4657   paraf.dxOffset = WRAP_INDENT;\r
4658   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4659   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4660   UpdateICSWidth(hText);\r
4661 }\r
4662 \r
4663 /*---------------------------------------------------------------------------*\\r
4664  *\r
4665  * Window Proc for main window\r
4666  *\r
4667 \*---------------------------------------------------------------------------*/\r
4668 \r
4669 /* Process messages for main window, etc. */\r
4670 LRESULT CALLBACK\r
4671 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4672 {\r
4673   FARPROC lpProc;\r
4674   int wmId, wmEvent;\r
4675   char *defName;\r
4676   FILE *f;\r
4677   UINT number;\r
4678   char fileTitle[MSG_SIZ];\r
4679   char buf[MSG_SIZ];\r
4680   static SnapData sd;\r
4681   static int peek=0;\r
4682 \r
4683   switch (message) {\r
4684 \r
4685   case WM_PAINT: /* message: repaint portion of window */\r
4686     PaintProc(hwnd);\r
4687     break;\r
4688 \r
4689   case WM_ERASEBKGND:\r
4690     if (IsIconic(hwnd)) {\r
4691       /* Cheat; change the message */\r
4692       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4693     } else {\r
4694       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4695     }\r
4696     break;\r
4697 \r
4698   case WM_LBUTTONDOWN:\r
4699   case WM_MBUTTONDOWN:\r
4700   case WM_RBUTTONDOWN:\r
4701   case WM_LBUTTONUP:\r
4702   case WM_MBUTTONUP:\r
4703   case WM_RBUTTONUP:\r
4704   case WM_MOUSEMOVE:\r
4705   case WM_MOUSEWHEEL:\r
4706     MouseEvent(hwnd, message, wParam, lParam);\r
4707     break;\r
4708 \r
4709   case WM_KEYUP:\r
4710     if((char)wParam == '\b') {\r
4711       ForwardEvent(); peek = 0;\r
4712     }\r
4713 \r
4714     JAWS_KBUP_NAVIGATION\r
4715 \r
4716     break;\r
4717 \r
4718   case WM_KEYDOWN:\r
4719     if((char)wParam == '\b') {\r
4720       if(!peek) BackwardEvent(), peek = 1;\r
4721     }\r
4722 \r
4723     JAWS_KBDOWN_NAVIGATION\r
4724 \r
4725     break;\r
4726 \r
4727   case WM_CHAR:\r
4728     \r
4729     JAWS_ALT_INTERCEPT\r
4730 \r
4731     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4732         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4733         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4734         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4735         SetFocus(h);\r
4736         SendMessage(h, message, wParam, lParam);\r
4737     } else if(lParam != KF_REPEAT) {\r
4738         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4739                 TypeInEvent((char)wParam);\r
4740         } else if((char)wParam == 003) CopyGameToClipboard();\r
4741          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4742     }\r
4743 \r
4744     break;\r
4745 \r
4746   case WM_PALETTECHANGED:\r
4747     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4748       int nnew;\r
4749       HDC hdc = GetDC(hwndMain);\r
4750       SelectPalette(hdc, hPal, TRUE);\r
4751       nnew = RealizePalette(hdc);\r
4752       if (nnew > 0) {\r
4753         paletteChanged = TRUE;\r
4754         InvalidateRect(hwnd, &boardRect, FALSE);\r
4755       }\r
4756       ReleaseDC(hwnd, hdc);\r
4757     }\r
4758     break;\r
4759 \r
4760   case WM_QUERYNEWPALETTE:\r
4761     if (!appData.monoMode /*&& paletteChanged*/) {\r
4762       int nnew;\r
4763       HDC hdc = GetDC(hwndMain);\r
4764       paletteChanged = FALSE;\r
4765       SelectPalette(hdc, hPal, FALSE);\r
4766       nnew = RealizePalette(hdc);\r
4767       if (nnew > 0) {\r
4768         InvalidateRect(hwnd, &boardRect, FALSE);\r
4769       }\r
4770       ReleaseDC(hwnd, hdc);\r
4771       return TRUE;\r
4772     }\r
4773     return FALSE;\r
4774 \r
4775   case WM_COMMAND: /* message: command from application menu */\r
4776     wmId    = LOWORD(wParam);\r
4777     wmEvent = HIWORD(wParam);\r
4778 \r
4779     switch (wmId) {\r
4780     case IDM_NewGame:\r
4781       ResetGameEvent();\r
4782       SAY("new game enter a move to play against the computer with white");\r
4783       break;\r
4784 \r
4785     case IDM_NewGameFRC:\r
4786       if( NewGameFRC() == 0 ) {\r
4787         ResetGameEvent();\r
4788       }\r
4789       break;\r
4790 \r
4791     case IDM_NewVariant:\r
4792       NewVariantPopup(hwnd);\r
4793       break;\r
4794 \r
4795     case IDM_LoadGame:\r
4796       LoadGameDialog(hwnd, _("Load Game from File"));\r
4797       break;\r
4798 \r
4799     case IDM_LoadNextGame:\r
4800       ReloadGame(1);\r
4801       break;\r
4802 \r
4803     case IDM_LoadPrevGame:\r
4804       ReloadGame(-1);\r
4805       break;\r
4806 \r
4807     case IDM_ReloadGame:\r
4808       ReloadGame(0);\r
4809       break;\r
4810 \r
4811     case IDM_LoadPosition:\r
4812       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4813         Reset(FALSE, TRUE);\r
4814       }\r
4815       number = 1;\r
4816       f = OpenFileDialog(hwnd, "rb", "",\r
4817                          appData.oldSaveStyle ? "pos" : "fen",\r
4818                          POSITION_FILT,\r
4819                          _("Load Position from File"), &number, fileTitle, NULL);\r
4820       if (f != NULL) {\r
4821         LoadPosition(f, number, fileTitle);\r
4822       }\r
4823       break;\r
4824 \r
4825     case IDM_LoadNextPosition:\r
4826       ReloadPosition(1);\r
4827       break;\r
4828 \r
4829     case IDM_LoadPrevPosition:\r
4830       ReloadPosition(-1);\r
4831       break;\r
4832 \r
4833     case IDM_ReloadPosition:\r
4834       ReloadPosition(0);\r
4835       break;\r
4836 \r
4837     case IDM_SaveGame:\r
4838       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4839       f = OpenFileDialog(hwnd, "a", defName,\r
4840                          appData.oldSaveStyle ? "gam" : "pgn",\r
4841                          GAME_FILT,\r
4842                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4843       if (f != NULL) {\r
4844         SaveGame(f, 0, "");\r
4845       }\r
4846       break;\r
4847 \r
4848     case IDM_SavePosition:\r
4849       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4850       f = OpenFileDialog(hwnd, "a", defName,\r
4851                          appData.oldSaveStyle ? "pos" : "fen",\r
4852                          POSITION_FILT,\r
4853                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4854       if (f != NULL) {\r
4855         SavePosition(f, 0, "");\r
4856       }\r
4857       break;\r
4858 \r
4859     case IDM_SaveDiagram:\r
4860       defName = "diagram";\r
4861       f = OpenFileDialog(hwnd, "wb", defName,\r
4862                          "bmp",\r
4863                          DIAGRAM_FILT,\r
4864                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4865       if (f != NULL) {\r
4866         SaveDiagram(f);\r
4867       }\r
4868       break;\r
4869 \r
4870     case IDM_CreateBook:\r
4871       CreateBookEvent();\r
4872       break;\r
4873 \r
4874     case IDM_CopyGame:\r
4875       CopyGameToClipboard();\r
4876       break;\r
4877 \r
4878     case IDM_PasteGame:\r
4879       PasteGameFromClipboard();\r
4880       break;\r
4881 \r
4882     case IDM_CopyGameListToClipboard:\r
4883       CopyGameListToClipboard();\r
4884       break;\r
4885 \r
4886     /* [AS] Autodetect FEN or PGN data */\r
4887     case IDM_PasteAny:\r
4888       PasteGameOrFENFromClipboard();\r
4889       break;\r
4890 \r
4891     /* [AS] Move history */\r
4892     case IDM_ShowMoveHistory:\r
4893         if( MoveHistoryIsUp() ) {\r
4894             MoveHistoryPopDown();\r
4895         }\r
4896         else {\r
4897             MoveHistoryPopUp();\r
4898         }\r
4899         break;\r
4900 \r
4901     /* [AS] Eval graph */\r
4902     case IDM_ShowEvalGraph:\r
4903         if( EvalGraphIsUp() ) {\r
4904             EvalGraphPopDown();\r
4905         }\r
4906         else {\r
4907             EvalGraphPopUp();\r
4908             SetFocus(hwndMain);\r
4909         }\r
4910         break;\r
4911 \r
4912     /* [AS] Engine output */\r
4913     case IDM_ShowEngineOutput:\r
4914         if( EngineOutputIsUp() ) {\r
4915             EngineOutputPopDown();\r
4916         }\r
4917         else {\r
4918             EngineOutputPopUp();\r
4919         }\r
4920         break;\r
4921 \r
4922     /* [AS] User adjudication */\r
4923     case IDM_UserAdjudication_White:\r
4924         UserAdjudicationEvent( +1 );\r
4925         break;\r
4926 \r
4927     case IDM_UserAdjudication_Black:\r
4928         UserAdjudicationEvent( -1 );\r
4929         break;\r
4930 \r
4931     case IDM_UserAdjudication_Draw:\r
4932         UserAdjudicationEvent( 0 );\r
4933         break;\r
4934 \r
4935     /* [AS] Game list options dialog */\r
4936     case IDM_GameListOptions:\r
4937       GameListOptions();\r
4938       break;\r
4939 \r
4940     case IDM_NewChat:\r
4941       ChatPopUp(NULL);\r
4942       break;\r
4943 \r
4944     case IDM_CopyPosition:\r
4945       CopyFENToClipboard();\r
4946       break;\r
4947 \r
4948     case IDM_PastePosition:\r
4949       PasteFENFromClipboard();\r
4950       break;\r
4951 \r
4952     case IDM_MailMove:\r
4953       MailMoveEvent();\r
4954       break;\r
4955 \r
4956     case IDM_ReloadCMailMsg:\r
4957       Reset(TRUE, TRUE);\r
4958       ReloadCmailMsgEvent(FALSE);\r
4959       break;\r
4960 \r
4961     case IDM_Minimize:\r
4962       ShowWindow(hwnd, SW_MINIMIZE);\r
4963       break;\r
4964 \r
4965     case IDM_Exit:\r
4966       ExitEvent(0);\r
4967       break;\r
4968 \r
4969     case IDM_MachineWhite:\r
4970       MachineWhiteEvent();\r
4971       /*\r
4972        * refresh the tags dialog only if it's visible\r
4973        */\r
4974       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4975           char *tags;\r
4976           tags = PGNTags(&gameInfo);\r
4977           TagsPopUp(tags, CmailMsg());\r
4978           free(tags);\r
4979       }\r
4980       SAY("computer starts playing white");\r
4981       break;\r
4982 \r
4983     case IDM_MachineBlack:\r
4984       MachineBlackEvent();\r
4985       /*\r
4986        * refresh the tags dialog only if it's visible\r
4987        */\r
4988       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4989           char *tags;\r
4990           tags = PGNTags(&gameInfo);\r
4991           TagsPopUp(tags, CmailMsg());\r
4992           free(tags);\r
4993       }\r
4994       SAY("computer starts playing black");\r
4995       break;\r
4996 \r
4997     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4998       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4999       break;\r
5000 \r
5001     case IDM_TwoMachines:\r
5002       TwoMachinesEvent();\r
5003       /*\r
5004        * refresh the tags dialog only if it's visible\r
5005        */\r
5006       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5007           char *tags;\r
5008           tags = PGNTags(&gameInfo);\r
5009           TagsPopUp(tags, CmailMsg());\r
5010           free(tags);\r
5011       }\r
5012       SAY("computer starts playing both sides");\r
5013       break;\r
5014 \r
5015     case IDM_AnalysisMode:\r
5016       if(AnalyzeModeEvent()) {\r
5017         SAY("analyzing current position");\r
5018       }\r
5019       break;\r
5020 \r
5021     case IDM_AnalyzeFile:\r
5022       AnalyzeFileEvent();\r
5023       break;\r
5024 \r
5025     case IDM_IcsClient:\r
5026       IcsClientEvent();\r
5027       break;\r
5028 \r
5029     case IDM_EditGame:\r
5030     case IDM_EditGame2:\r
5031       EditGameEvent();\r
5032       SAY("edit game");\r
5033       break;\r
5034 \r
5035     case IDM_EditPosition:\r
5036     case IDM_EditPosition2:\r
5037       EditPositionEvent();\r
5038       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5039       break;\r
5040 \r
5041     case IDM_Training:\r
5042       TrainingEvent();\r
5043       break;\r
5044 \r
5045     case IDM_ShowGameList:\r
5046       ShowGameListProc();\r
5047       break;\r
5048 \r
5049     case IDM_EditProgs1:\r
5050       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5051       break;\r
5052 \r
5053     case IDM_LoadProg1:\r
5054      LoadEnginePopUp(hwndMain, 0);\r
5055       break;\r
5056 \r
5057     case IDM_LoadProg2:\r
5058      LoadEnginePopUp(hwndMain, 1);\r
5059       break;\r
5060 \r
5061     case IDM_EditServers:\r
5062       EditTagsPopUp(icsNames, &icsNames);\r
5063       break;\r
5064 \r
5065     case IDM_EditTags:\r
5066     case IDM_Tags:\r
5067       EditTagsProc();\r
5068       break;\r
5069 \r
5070     case IDM_EditBook:\r
5071       EditBookEvent();\r
5072       break;\r
5073 \r
5074     case IDM_EditComment:\r
5075     case IDM_Comment:\r
5076       if (commentUp && editComment) {\r
5077         CommentPopDown();\r
5078       } else {\r
5079         EditCommentEvent();\r
5080       }\r
5081       break;\r
5082 \r
5083     case IDM_Pause:\r
5084       PauseEvent();\r
5085       break;\r
5086 \r
5087     case IDM_Accept:\r
5088       AcceptEvent();\r
5089       break;\r
5090 \r
5091     case IDM_Decline:\r
5092       DeclineEvent();\r
5093       break;\r
5094 \r
5095     case IDM_Rematch:\r
5096       RematchEvent();\r
5097       break;\r
5098 \r
5099     case IDM_CallFlag:\r
5100       CallFlagEvent();\r
5101       break;\r
5102 \r
5103     case IDM_Draw:\r
5104       DrawEvent();\r
5105       break;\r
5106 \r
5107     case IDM_Adjourn:\r
5108       AdjournEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Abort:\r
5112       AbortEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Resign:\r
5116       ResignEvent();\r
5117       break;\r
5118 \r
5119     case IDM_StopObserving:\r
5120       StopObservingEvent();\r
5121       break;\r
5122 \r
5123     case IDM_StopExamining:\r
5124       StopExaminingEvent();\r
5125       break;\r
5126 \r
5127     case IDM_Upload:\r
5128       UploadGameEvent();\r
5129       break;\r
5130 \r
5131     case IDM_TypeInMove:\r
5132       TypeInEvent('\000');\r
5133       break;\r
5134 \r
5135     case IDM_TypeInName:\r
5136       PopUpNameDialog('\000');\r
5137       break;\r
5138 \r
5139     case IDM_Backward:\r
5140       BackwardEvent();\r
5141       SetFocus(hwndMain);\r
5142       break;\r
5143 \r
5144     JAWS_MENU_ITEMS\r
5145 \r
5146     case IDM_Forward:\r
5147       ForwardEvent();\r
5148       SetFocus(hwndMain);\r
5149       break;\r
5150 \r
5151     case IDM_ToStart:\r
5152       ToStartEvent();\r
5153       SetFocus(hwndMain);\r
5154       break;\r
5155 \r
5156     case IDM_ToEnd:\r
5157       ToEndEvent();\r
5158       SetFocus(hwndMain);\r
5159       break;\r
5160 \r
5161     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5162     case OPT_GameListPrev:\r
5163       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5164       break;\r
5165 \r
5166     case IDM_Revert:\r
5167       RevertEvent(FALSE);\r
5168       break;\r
5169 \r
5170     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5171       RevertEvent(TRUE);\r
5172       break;\r
5173 \r
5174     case IDM_TruncateGame:\r
5175       TruncateGameEvent();\r
5176       break;\r
5177 \r
5178     case IDM_MoveNow:\r
5179       MoveNowEvent();\r
5180       break;\r
5181 \r
5182     case IDM_RetractMove:\r
5183       RetractMoveEvent();\r
5184       break;\r
5185 \r
5186     case IDM_FlipView:\r
5187       flipView = !flipView;\r
5188       DrawPosition(FALSE, NULL);\r
5189       break;\r
5190 \r
5191     case IDM_FlipClock:\r
5192       flipClock = !flipClock;\r
5193       DisplayBothClocks();\r
5194       DisplayLogos();\r
5195       break;\r
5196 \r
5197     case IDM_MuteSounds:\r
5198       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5199       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5200                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5201       break;\r
5202 \r
5203     case IDM_GeneralOptions:\r
5204       GeneralOptionsPopup(hwnd);\r
5205       DrawPosition(TRUE, NULL);\r
5206       break;\r
5207 \r
5208     case IDM_BoardOptions:\r
5209       BoardOptionsPopup(hwnd);\r
5210       break;\r
5211 \r
5212     case IDM_ThemeOptions:\r
5213       ThemeOptionsPopup(hwnd);\r
5214       break;\r
5215 \r
5216     case IDM_EnginePlayOptions:\r
5217       EnginePlayOptionsPopup(hwnd);\r
5218       break;\r
5219 \r
5220     case IDM_Engine1Options:\r
5221       EngineOptionsPopup(hwnd, &first);\r
5222       break;\r
5223 \r
5224     case IDM_Engine2Options:\r
5225       savedHwnd = hwnd;\r
5226       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5227       EngineOptionsPopup(hwnd, &second);\r
5228       break;\r
5229 \r
5230     case IDM_OptionsUCI:\r
5231       UciOptionsPopup(hwnd);\r
5232       break;\r
5233 \r
5234     case IDM_Tourney:\r
5235       TourneyPopup(hwnd);\r
5236       break;\r
5237 \r
5238     case IDM_IcsOptions:\r
5239       IcsOptionsPopup(hwnd);\r
5240       break;\r
5241 \r
5242     case IDM_Fonts:\r
5243       FontsOptionsPopup(hwnd);\r
5244       break;\r
5245 \r
5246     case IDM_Sounds:\r
5247       SoundOptionsPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_CommPort:\r
5251       CommPortOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_LoadOptions:\r
5255       LoadOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_SaveOptions:\r
5259       SaveOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_TimeControl:\r
5263       TimeControlOptionsPopup(hwnd);\r
5264       break;\r
5265 \r
5266     case IDM_SaveSettings:\r
5267       SaveSettings(settingsFileName);\r
5268       break;\r
5269 \r
5270     case IDM_SaveSettingsOnExit:\r
5271       saveSettingsOnExit = !saveSettingsOnExit;\r
5272       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5273                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5274                                          MF_CHECKED : MF_UNCHECKED));\r
5275       break;\r
5276 \r
5277     case IDM_Hint:\r
5278       HintEvent();\r
5279       break;\r
5280 \r
5281     case IDM_Book:\r
5282       BookEvent();\r
5283       break;\r
5284 \r
5285     case IDM_AboutGame:\r
5286       AboutGameEvent();\r
5287       break;\r
5288 \r
5289     case IDM_Debug:\r
5290       appData.debugMode = !appData.debugMode;\r
5291       if (appData.debugMode) {\r
5292         char dir[MSG_SIZ];\r
5293         GetCurrentDirectory(MSG_SIZ, dir);\r
5294         SetCurrentDirectory(installDir);\r
5295         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5296         SetCurrentDirectory(dir);\r
5297         setbuf(debugFP, NULL);\r
5298       } else {\r
5299         fclose(debugFP);\r
5300         debugFP = NULL;\r
5301       }\r
5302       break;\r
5303 \r
5304     case IDM_HELPCONTENTS:\r
5305       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5306           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5307           MessageBox (GetFocus(),\r
5308                     _("Unable to activate help"),\r
5309                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5310       }\r
5311       break;\r
5312 \r
5313     case IDM_HELPSEARCH:\r
5314         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5315             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5316         MessageBox (GetFocus(),\r
5317                     _("Unable to activate help"),\r
5318                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5319       }\r
5320       break;\r
5321 \r
5322     case IDM_HELPHELP:\r
5323       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5324         MessageBox (GetFocus(),\r
5325                     _("Unable to activate help"),\r
5326                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5327       }\r
5328       break;\r
5329 \r
5330     case IDM_ABOUT:\r
5331       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5332       DialogBox(hInst, \r
5333         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5334         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5335       FreeProcInstance(lpProc);\r
5336       break;\r
5337 \r
5338     case IDM_DirectCommand1:\r
5339       AskQuestionEvent(_("Direct Command"),\r
5340                        _("Send to chess program:"), "", "1");\r
5341       break;\r
5342     case IDM_DirectCommand2:\r
5343       AskQuestionEvent(_("Direct Command"),\r
5344                        _("Send to second chess program:"), "", "2");\r
5345       break;\r
5346 \r
5347     case EP_WhitePawn:\r
5348       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_WhiteKnight:\r
5353       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_WhiteBishop:\r
5358       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case EP_WhiteRook:\r
5363       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case EP_WhiteQueen:\r
5368       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case EP_WhiteFerz:\r
5373       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case EP_WhiteWazir:\r
5378       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case EP_WhiteAlfil:\r
5383       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case EP_WhiteCannon:\r
5388       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case EP_WhiteCardinal:\r
5393       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case EP_WhiteMarshall:\r
5398       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case EP_WhiteKing:\r
5403       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case EP_BlackPawn:\r
5408       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case EP_BlackKnight:\r
5413       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case EP_BlackBishop:\r
5418       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5419       fromX = fromY = -1;\r
5420       break;\r
5421 \r
5422     case EP_BlackRook:\r
5423       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5424       fromX = fromY = -1;\r
5425       break;\r
5426 \r
5427     case EP_BlackQueen:\r
5428       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5429       fromX = fromY = -1;\r
5430       break;\r
5431 \r
5432     case EP_BlackFerz:\r
5433       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5434       fromX = fromY = -1;\r
5435       break;\r
5436 \r
5437     case EP_BlackWazir:\r
5438       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5439       fromX = fromY = -1;\r
5440       break;\r
5441 \r
5442     case EP_BlackAlfil:\r
5443       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5444       fromX = fromY = -1;\r
5445       break;\r
5446 \r
5447     case EP_BlackCannon:\r
5448       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5449       fromX = fromY = -1;\r
5450       break;\r
5451 \r
5452     case EP_BlackCardinal:\r
5453       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5454       fromX = fromY = -1;\r
5455       break;\r
5456 \r
5457     case EP_BlackMarshall:\r
5458       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case EP_BlackKing:\r
5463       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5464       fromX = fromY = -1;\r
5465       break;\r
5466 \r
5467     case EP_EmptySquare:\r
5468       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5469       fromX = fromY = -1;\r
5470       break;\r
5471 \r
5472     case EP_ClearBoard:\r
5473       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5474       fromX = fromY = -1;\r
5475       break;\r
5476 \r
5477     case EP_White:\r
5478       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5479       fromX = fromY = -1;\r
5480       break;\r
5481 \r
5482     case EP_Black:\r
5483       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5484       fromX = fromY = -1;\r
5485       break;\r
5486 \r
5487     case EP_Promote:\r
5488       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5489       fromX = fromY = -1;\r
5490       break;\r
5491 \r
5492     case EP_Demote:\r
5493       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5494       fromX = fromY = -1;\r
5495       break;\r
5496 \r
5497     case DP_Pawn:\r
5498       DropMenuEvent(WhitePawn, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case DP_Knight:\r
5503       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case DP_Bishop:\r
5508       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case DP_Rook:\r
5513       DropMenuEvent(WhiteRook, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case DP_Queen:\r
5518       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case IDM_English:\r
5523       barbaric = 0; appData.language = "";\r
5524       TranslateMenus(0);\r
5525       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5526       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5527       lastChecked = wmId;\r
5528       break;\r
5529 \r
5530     default:\r
5531       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5532           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5533       else\r
5534       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5535           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5536           TranslateMenus(0);\r
5537           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5538           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5539           lastChecked = wmId;\r
5540           break;\r
5541       }\r
5542       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5543     }\r
5544     break;\r
5545 \r
5546   case WM_TIMER:\r
5547     switch (wParam) {\r
5548     case CLOCK_TIMER_ID:\r
5549       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5550       clockTimerEvent = 0;\r
5551       DecrementClocks(); /* call into back end */\r
5552       break;\r
5553     case LOAD_GAME_TIMER_ID:\r
5554       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5555       loadGameTimerEvent = 0;\r
5556       AutoPlayGameLoop(); /* call into back end */\r
5557       break;\r
5558     case ANALYSIS_TIMER_ID:\r
5559       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5560                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5561         AnalysisPeriodicEvent(0);\r
5562       } else {\r
5563         KillTimer(hwnd, analysisTimerEvent);\r
5564         analysisTimerEvent = 0;\r
5565       }\r
5566       break;\r
5567     case DELAYED_TIMER_ID:\r
5568       KillTimer(hwnd, delayedTimerEvent);\r
5569       delayedTimerEvent = 0;\r
5570       delayedTimerCallback();\r
5571       break;\r
5572     }\r
5573     break;\r
5574 \r
5575   case WM_USER_Input:\r
5576     InputEvent(hwnd, message, wParam, lParam);\r
5577     break;\r
5578 \r
5579   /* [AS] Also move "attached" child windows */\r
5580   case WM_WINDOWPOSCHANGING:\r
5581 \r
5582     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5583         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5584 \r
5585         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5586             /* Window is moving */\r
5587             RECT rcMain;\r
5588 \r
5589 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5590             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5591             rcMain.right  = wpMain.x + wpMain.width;\r
5592             rcMain.top    = wpMain.y;\r
5593             rcMain.bottom = wpMain.y + wpMain.height;\r
5594             \r
5595             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5596             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5597             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5598             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5599             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5600             wpMain.x = lpwp->x;\r
5601             wpMain.y = lpwp->y;\r
5602         }\r
5603     }\r
5604     break;\r
5605 \r
5606   /* [AS] Snapping */\r
5607   case WM_ENTERSIZEMOVE:\r
5608     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5609     if (hwnd == hwndMain) {\r
5610       doingSizing = TRUE;\r
5611       lastSizing = 0;\r
5612     }\r
5613     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5614     break;\r
5615 \r
5616   case WM_SIZING:\r
5617     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5618     if (hwnd == hwndMain) {\r
5619       lastSizing = wParam;\r
5620     }\r
5621     break;\r
5622 \r
5623   case WM_MOVING:\r
5624     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5625       return OnMoving( &sd, hwnd, wParam, lParam );\r
5626 \r
5627   case WM_EXITSIZEMOVE:\r
5628     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5629     if (hwnd == hwndMain) {\r
5630       RECT client;\r
5631       doingSizing = FALSE;\r
5632       InvalidateRect(hwnd, &boardRect, FALSE);\r
5633       GetClientRect(hwnd, &client);\r
5634       ResizeBoard(client.right, client.bottom, lastSizing);\r
5635       lastSizing = 0;\r
5636       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5637     }\r
5638     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5639     break;\r
5640 \r
5641   case WM_DESTROY: /* message: window being destroyed */\r
5642     PostQuitMessage(0);\r
5643     break;\r
5644 \r
5645   case WM_CLOSE:\r
5646     if (hwnd == hwndMain) {\r
5647       ExitEvent(0);\r
5648     }\r
5649     break;\r
5650 \r
5651   default:      /* Passes it on if unprocessed */\r
5652     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5653   }\r
5654   return 0;\r
5655 }\r
5656 \r
5657 /*---------------------------------------------------------------------------*\\r
5658  *\r
5659  * Misc utility routines\r
5660  *\r
5661 \*---------------------------------------------------------------------------*/\r
5662 \r
5663 /*\r
5664  * Decent random number generator, at least not as bad as Windows\r
5665  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5666  */\r
5667 unsigned int randstate;\r
5668 \r
5669 int\r
5670 myrandom(void)\r
5671 {\r
5672   randstate = randstate * 1664525 + 1013904223;\r
5673   return (int) randstate & 0x7fffffff;\r
5674 }\r
5675 \r
5676 void\r
5677 mysrandom(unsigned int seed)\r
5678 {\r
5679   randstate = seed;\r
5680 }\r
5681 \r
5682 \r
5683 /* \r
5684  * returns TRUE if user selects a different color, FALSE otherwise \r
5685  */\r
5686 \r
5687 BOOL\r
5688 ChangeColor(HWND hwnd, COLORREF *which)\r
5689 {\r
5690   static BOOL firstTime = TRUE;\r
5691   static DWORD customColors[16];\r
5692   CHOOSECOLOR cc;\r
5693   COLORREF newcolor;\r
5694   int i;\r
5695   ColorClass ccl;\r
5696 \r
5697   if (firstTime) {\r
5698     /* Make initial colors in use available as custom colors */\r
5699     /* Should we put the compiled-in defaults here instead? */\r
5700     i = 0;\r
5701     customColors[i++] = lightSquareColor & 0xffffff;\r
5702     customColors[i++] = darkSquareColor & 0xffffff;\r
5703     customColors[i++] = whitePieceColor & 0xffffff;\r
5704     customColors[i++] = blackPieceColor & 0xffffff;\r
5705     customColors[i++] = highlightSquareColor & 0xffffff;\r
5706     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5707 \r
5708     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5709       customColors[i++] = textAttribs[ccl].color;\r
5710     }\r
5711     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5712     firstTime = FALSE;\r
5713   }\r
5714 \r
5715   cc.lStructSize = sizeof(cc);\r
5716   cc.hwndOwner = hwnd;\r
5717   cc.hInstance = NULL;\r
5718   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5719   cc.lpCustColors = (LPDWORD) customColors;\r
5720   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5721 \r
5722   if (!ChooseColor(&cc)) return FALSE;\r
5723 \r
5724   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5725   if (newcolor == *which) return FALSE;\r
5726   *which = newcolor;\r
5727   return TRUE;\r
5728 \r
5729   /*\r
5730   InitDrawingColors();\r
5731   InvalidateRect(hwnd, &boardRect, FALSE);\r
5732   */\r
5733 }\r
5734 \r
5735 BOOLEAN\r
5736 MyLoadSound(MySound *ms)\r
5737 {\r
5738   BOOL ok = FALSE;\r
5739   struct stat st;\r
5740   FILE *f;\r
5741 \r
5742   if (ms->data && ms->flag) free(ms->data);\r
5743   ms->data = NULL;\r
5744 \r
5745   switch (ms->name[0]) {\r
5746   case NULLCHAR:\r
5747     /* Silence */\r
5748     ok = TRUE;\r
5749     break;\r
5750   case '$':\r
5751     /* System sound from Control Panel.  Don't preload here. */\r
5752     ok = TRUE;\r
5753     break;\r
5754   case '!':\r
5755     if (ms->name[1] == NULLCHAR) {\r
5756       /* "!" alone = silence */\r
5757       ok = TRUE;\r
5758     } else {\r
5759       /* Builtin wave resource.  Error if not found. */\r
5760       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5761       if (h == NULL) break;\r
5762       ms->data = (void *)LoadResource(hInst, h);\r
5763       ms->flag = 0; // not maloced, so cannot be freed!\r
5764       if (h == NULL) break;\r
5765       ok = TRUE;\r
5766     }\r
5767     break;\r
5768   default:\r
5769     /* .wav file.  Error if not found. */\r
5770     f = fopen(ms->name, "rb");\r
5771     if (f == NULL) break;\r
5772     if (fstat(fileno(f), &st) < 0) break;\r
5773     ms->data = malloc(st.st_size);\r
5774     ms->flag = 1;\r
5775     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5776     fclose(f);\r
5777     ok = TRUE;\r
5778     break;\r
5779   }\r
5780   if (!ok) {\r
5781     char buf[MSG_SIZ];\r
5782       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5783     DisplayError(buf, GetLastError());\r
5784   }\r
5785   return ok;\r
5786 }\r
5787 \r
5788 BOOLEAN\r
5789 MyPlaySound(MySound *ms)\r
5790 {\r
5791   BOOLEAN ok = FALSE;\r
5792 \r
5793   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5794   switch (ms->name[0]) {\r
5795   case NULLCHAR:\r
5796         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5797     /* Silence */\r
5798     ok = TRUE;\r
5799     break;\r
5800   case '$':\r
5801     /* System sound from Control Panel (deprecated feature).\r
5802        "$" alone or an unset sound name gets default beep (still in use). */\r
5803     if (ms->name[1]) {\r
5804       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5805     }\r
5806     if (!ok) ok = MessageBeep(MB_OK);\r
5807     break; \r
5808   case '!':\r
5809     /* Builtin wave resource, or "!" alone for silence */\r
5810     if (ms->name[1]) {\r
5811       if (ms->data == NULL) return FALSE;\r
5812       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5813     } else {\r
5814       ok = TRUE;\r
5815     }\r
5816     break;\r
5817   default:\r
5818     /* .wav file.  Error if not found. */\r
5819     if (ms->data == NULL) return FALSE;\r
5820     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5821     break;\r
5822   }\r
5823   /* Don't print an error: this can happen innocently if the sound driver\r
5824      is busy; for instance, if another instance of WinBoard is playing\r
5825      a sound at about the same time. */\r
5826   return ok;\r
5827 }\r
5828 \r
5829 \r
5830 LRESULT CALLBACK\r
5831 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5832 {\r
5833   BOOL ok;\r
5834   OPENFILENAME *ofn;\r
5835   static UINT *number; /* gross that this is static */\r
5836 \r
5837   switch (message) {\r
5838   case WM_INITDIALOG: /* message: initialize dialog box */\r
5839     /* Center the dialog over the application window */\r
5840     ofn = (OPENFILENAME *) lParam;\r
5841     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5842       number = (UINT *) ofn->lCustData;\r
5843       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5844     } else {\r
5845       number = NULL;\r
5846     }\r
5847     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5848     Translate(hDlg, 1536);\r
5849     return FALSE;  /* Allow for further processing */\r
5850 \r
5851   case WM_COMMAND:\r
5852     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5853       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5854     }\r
5855     return FALSE;  /* Allow for further processing */\r
5856   }\r
5857   return FALSE;\r
5858 }\r
5859 \r
5860 UINT APIENTRY\r
5861 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5862 {\r
5863   static UINT *number;\r
5864   OPENFILENAME *ofname;\r
5865   OFNOTIFY *ofnot;\r
5866   switch (uiMsg) {\r
5867   case WM_INITDIALOG:\r
5868     Translate(hdlg, DLG_IndexNumber);\r
5869     ofname = (OPENFILENAME *)lParam;\r
5870     number = (UINT *)(ofname->lCustData);\r
5871     break;\r
5872   case WM_NOTIFY:\r
5873     ofnot = (OFNOTIFY *)lParam;\r
5874     if (ofnot->hdr.code == CDN_FILEOK) {\r
5875       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5876     }\r
5877     break;\r
5878   }\r
5879   return 0;\r
5880 }\r
5881 \r
5882 \r
5883 FILE *\r
5884 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5885                char *nameFilt, char *dlgTitle, UINT *number,\r
5886                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5887 {\r
5888   OPENFILENAME openFileName;\r
5889   char buf1[MSG_SIZ];\r
5890   FILE *f;\r
5891 \r
5892   if (fileName == NULL) fileName = buf1;\r
5893   if (defName == NULL) {\r
5894     safeStrCpy(fileName, "*.", 3 );\r
5895     strcat(fileName, defExt);\r
5896   } else {\r
5897     safeStrCpy(fileName, defName, MSG_SIZ );\r
5898   }\r
5899     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5900   if (number) *number = 0;\r
5901 \r
5902   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5903   openFileName.hwndOwner         = hwnd;\r
5904   openFileName.hInstance         = (HANDLE) hInst;\r
5905   openFileName.lpstrFilter       = nameFilt;\r
5906   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5907   openFileName.nMaxCustFilter    = 0L;\r
5908   openFileName.nFilterIndex      = 1L;\r
5909   openFileName.lpstrFile         = fileName;\r
5910   openFileName.nMaxFile          = MSG_SIZ;\r
5911   openFileName.lpstrFileTitle    = fileTitle;\r
5912   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5913   openFileName.lpstrInitialDir   = NULL;\r
5914   openFileName.lpstrTitle        = dlgTitle;\r
5915   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5916     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5917     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5918     | (oldDialog ? 0 : OFN_EXPLORER);\r
5919   openFileName.nFileOffset       = 0;\r
5920   openFileName.nFileExtension    = 0;\r
5921   openFileName.lpstrDefExt       = defExt;\r
5922   openFileName.lCustData         = (LONG) number;\r
5923   openFileName.lpfnHook          = oldDialog ?\r
5924     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5925   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5926 \r
5927   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5928                         GetOpenFileName(&openFileName)) {\r
5929     /* open the file */\r
5930     f = fopen(openFileName.lpstrFile, write);\r
5931     if (f == NULL) {\r
5932       MessageBox(hwnd, _("File open failed"), NULL,\r
5933                  MB_OK|MB_ICONEXCLAMATION);\r
5934       return NULL;\r
5935     }\r
5936   } else {\r
5937     int err = CommDlgExtendedError();\r
5938     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5939     return FALSE;\r
5940   }\r
5941   return f;\r
5942 }\r
5943 \r
5944 \r
5945 \r
5946 VOID APIENTRY\r
5947 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5948 {\r
5949   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5950 \r
5951   /*\r
5952    * Get the first pop-up menu in the menu template. This is the\r
5953    * menu that TrackPopupMenu displays.\r
5954    */\r
5955   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5956   TranslateOneMenu(10, hmenuTrackPopup);\r
5957 \r
5958   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5959 \r
5960   /*\r
5961    * TrackPopup uses screen coordinates, so convert the\r
5962    * coordinates of the mouse click to screen coordinates.\r
5963    */\r
5964   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5965 \r
5966   /* Draw and track the floating pop-up menu. */\r
5967   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5968                  pt.x, pt.y, 0, hwnd, NULL);\r
5969 \r
5970   /* Destroy the menu.*/\r
5971   DestroyMenu(hmenu);\r
5972 }\r
5973    \r
5974 typedef struct {\r
5975   HWND hDlg, hText;\r
5976   int sizeX, sizeY, newSizeX, newSizeY;\r
5977   HDWP hdwp;\r
5978 } ResizeEditPlusButtonsClosure;\r
5979 \r
5980 BOOL CALLBACK\r
5981 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5982 {\r
5983   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5984   RECT rect;\r
5985   POINT pt;\r
5986 \r
5987   if (hChild == cl->hText) return TRUE;\r
5988   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5989   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5990   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5991   ScreenToClient(cl->hDlg, &pt);\r
5992   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5993     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5994   return TRUE;\r
5995 }\r
5996 \r
5997 /* Resize a dialog that has a (rich) edit field filling most of\r
5998    the top, with a row of buttons below */\r
5999 VOID\r
6000 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6001 {\r
6002   RECT rectText;\r
6003   int newTextHeight, newTextWidth;\r
6004   ResizeEditPlusButtonsClosure cl;\r
6005   \r
6006   /*if (IsIconic(hDlg)) return;*/\r
6007   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6008   \r
6009   cl.hdwp = BeginDeferWindowPos(8);\r
6010 \r
6011   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6012   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6013   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6014   if (newTextHeight < 0) {\r
6015     newSizeY += -newTextHeight;\r
6016     newTextHeight = 0;\r
6017   }\r
6018   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6019     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6020 \r
6021   cl.hDlg = hDlg;\r
6022   cl.hText = hText;\r
6023   cl.sizeX = sizeX;\r
6024   cl.sizeY = sizeY;\r
6025   cl.newSizeX = newSizeX;\r
6026   cl.newSizeY = newSizeY;\r
6027   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6028 \r
6029   EndDeferWindowPos(cl.hdwp);\r
6030 }\r
6031 \r
6032 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6033 {\r
6034     RECT    rChild, rParent;\r
6035     int     wChild, hChild, wParent, hParent;\r
6036     int     wScreen, hScreen, xNew, yNew;\r
6037     HDC     hdc;\r
6038 \r
6039     /* Get the Height and Width of the child window */\r
6040     GetWindowRect (hwndChild, &rChild);\r
6041     wChild = rChild.right - rChild.left;\r
6042     hChild = rChild.bottom - rChild.top;\r
6043 \r
6044     /* Get the Height and Width of the parent window */\r
6045     GetWindowRect (hwndParent, &rParent);\r
6046     wParent = rParent.right - rParent.left;\r
6047     hParent = rParent.bottom - rParent.top;\r
6048 \r
6049     /* Get the display limits */\r
6050     hdc = GetDC (hwndChild);\r
6051     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6052     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6053     ReleaseDC(hwndChild, hdc);\r
6054 \r
6055     /* Calculate new X position, then adjust for screen */\r
6056     xNew = rParent.left + ((wParent - wChild) /2);\r
6057     if (xNew < 0) {\r
6058         xNew = 0;\r
6059     } else if ((xNew+wChild) > wScreen) {\r
6060         xNew = wScreen - wChild;\r
6061     }\r
6062 \r
6063     /* Calculate new Y position, then adjust for screen */\r
6064     if( mode == 0 ) {\r
6065         yNew = rParent.top  + ((hParent - hChild) /2);\r
6066     }\r
6067     else {\r
6068         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6069     }\r
6070 \r
6071     if (yNew < 0) {\r
6072         yNew = 0;\r
6073     } else if ((yNew+hChild) > hScreen) {\r
6074         yNew = hScreen - hChild;\r
6075     }\r
6076 \r
6077     /* Set it, and return */\r
6078     return SetWindowPos (hwndChild, NULL,\r
6079                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6080 }\r
6081 \r
6082 /* Center one window over another */\r
6083 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6084 {\r
6085     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6086 }\r
6087 \r
6088 /*---------------------------------------------------------------------------*\\r
6089  *\r
6090  * Startup Dialog functions\r
6091  *\r
6092 \*---------------------------------------------------------------------------*/\r
6093 void\r
6094 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6095 {\r
6096   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6097 \r
6098   while (*cd != NULL) {\r
6099     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6100     cd++;\r
6101   }\r
6102 }\r
6103 \r
6104 void\r
6105 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6106 {\r
6107   char buf1[MAX_ARG_LEN];\r
6108   int len;\r
6109 \r
6110   if (str[0] == '@') {\r
6111     FILE* f = fopen(str + 1, "r");\r
6112     if (f == NULL) {\r
6113       DisplayFatalError(str + 1, errno, 2);\r
6114       return;\r
6115     }\r
6116     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6117     fclose(f);\r
6118     buf1[len] = NULLCHAR;\r
6119     str = buf1;\r
6120   }\r
6121 \r
6122   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6123 \r
6124   for (;;) {\r
6125     char buf[MSG_SIZ];\r
6126     char *end = strchr(str, '\n');\r
6127     if (end == NULL) return;\r
6128     memcpy(buf, str, end - str);\r
6129     buf[end - str] = NULLCHAR;\r
6130     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6131     str = end + 1;\r
6132   }\r
6133 }\r
6134 \r
6135 void\r
6136 SetStartupDialogEnables(HWND hDlg)\r
6137 {\r
6138   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6139     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6140     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6141   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6142     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6143   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6144     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6145   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6146     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6147   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6148     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6149     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6150     IsDlgButtonChecked(hDlg, OPT_View));\r
6151 }\r
6152 \r
6153 char *\r
6154 QuoteForFilename(char *filename)\r
6155 {\r
6156   int dquote, space;\r
6157   dquote = strchr(filename, '"') != NULL;\r
6158   space = strchr(filename, ' ') != NULL;\r
6159   if (dquote || space) {\r
6160     if (dquote) {\r
6161       return "'";\r
6162     } else {\r
6163       return "\"";\r
6164     }\r
6165   } else {\r
6166     return "";\r
6167   }\r
6168 }\r
6169 \r
6170 VOID\r
6171 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6172 {\r
6173   char buf[MSG_SIZ];\r
6174   char *q;\r
6175 \r
6176   InitComboStringsFromOption(hwndCombo, nthnames);\r
6177   q = QuoteForFilename(nthcp);\r
6178     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6179   if (*nthdir != NULLCHAR) {\r
6180     q = QuoteForFilename(nthdir);\r
6181       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6182   }\r
6183   if (*nthcp == NULLCHAR) {\r
6184     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6185   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6186     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6187     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6188   }\r
6189 }\r
6190 \r
6191 LRESULT CALLBACK\r
6192 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6193 {\r
6194   char buf[MSG_SIZ];\r
6195   HANDLE hwndCombo;\r
6196   char *p;\r
6197 \r
6198   switch (message) {\r
6199   case WM_INITDIALOG:\r
6200     /* Center the dialog */\r
6201     CenterWindow (hDlg, GetDesktopWindow());\r
6202     Translate(hDlg, DLG_Startup);\r
6203     /* Initialize the dialog items */\r
6204     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6205                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6206                   firstChessProgramNames);\r
6207     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6208                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6209                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6210     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6211     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6212       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6213     if (*appData.icsHelper != NULLCHAR) {\r
6214       char *q = QuoteForFilename(appData.icsHelper);\r
6215       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6216     }\r
6217     if (*appData.icsHost == NULLCHAR) {\r
6218       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6219       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6220     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6221       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6222       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6223     }\r
6224 \r
6225     if (appData.icsActive) {\r
6226       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6227     }\r
6228     else if (appData.noChessProgram) {\r
6229       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6230     }\r
6231     else {\r
6232       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6233     }\r
6234 \r
6235     SetStartupDialogEnables(hDlg);\r
6236     return TRUE;\r
6237 \r
6238   case WM_COMMAND:\r
6239     switch (LOWORD(wParam)) {\r
6240     case IDOK:\r
6241       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6242         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6243         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6244         p = buf;\r
6245         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6246         ParseArgs(StringGet, &p);\r
6247         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6248         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6249         p = buf;\r
6250         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6251         ParseArgs(StringGet, &p);\r
6252         SwapEngines(singleList); // ... and then make it 'second'\r
6253         appData.noChessProgram = FALSE;\r
6254         appData.icsActive = FALSE;\r
6255       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6256         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6257         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6258         p = buf;\r
6259         ParseArgs(StringGet, &p);\r
6260         if (appData.zippyPlay) {\r
6261           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6262           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6263           p = buf;\r
6264           ParseArgs(StringGet, &p);\r
6265         }\r
6266       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6267         appData.noChessProgram = TRUE;\r
6268         appData.icsActive = FALSE;\r
6269       } else {\r
6270         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6271                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6272         return TRUE;\r
6273       }\r
6274       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6275         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6276         p = buf;\r
6277         ParseArgs(StringGet, &p);\r
6278       }\r
6279       EndDialog(hDlg, TRUE);\r
6280       return TRUE;\r
6281 \r
6282     case IDCANCEL:\r
6283       ExitEvent(0);\r
6284       return TRUE;\r
6285 \r
6286     case IDM_HELPCONTENTS:\r
6287       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6288         MessageBox (GetFocus(),\r
6289                     _("Unable to activate help"),\r
6290                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6291       }\r
6292       break;\r
6293 \r
6294     default:\r
6295       SetStartupDialogEnables(hDlg);\r
6296       break;\r
6297     }\r
6298     break;\r
6299   }\r
6300   return FALSE;\r
6301 }\r
6302 \r
6303 /*---------------------------------------------------------------------------*\\r
6304  *\r
6305  * About box dialog functions\r
6306  *\r
6307 \*---------------------------------------------------------------------------*/\r
6308 \r
6309 /* Process messages for "About" dialog box */\r
6310 LRESULT CALLBACK\r
6311 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6312 {\r
6313   switch (message) {\r
6314   case WM_INITDIALOG: /* message: initialize dialog box */\r
6315     /* Center the dialog over the application window */\r
6316     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6317     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6318     Translate(hDlg, ABOUTBOX);\r
6319     JAWS_COPYRIGHT\r
6320     return (TRUE);\r
6321 \r
6322   case WM_COMMAND: /* message: received a command */\r
6323     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6324         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6325       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6326       return (TRUE);\r
6327     }\r
6328     break;\r
6329   }\r
6330   return (FALSE);\r
6331 }\r
6332 \r
6333 /*---------------------------------------------------------------------------*\\r
6334  *\r
6335  * Comment Dialog functions\r
6336  *\r
6337 \*---------------------------------------------------------------------------*/\r
6338 \r
6339 LRESULT CALLBACK\r
6340 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6341 {\r
6342   static HANDLE hwndText = NULL;\r
6343   int len, newSizeX, newSizeY, flags;\r
6344   static int sizeX, sizeY;\r
6345   char *str;\r
6346   RECT rect;\r
6347   MINMAXINFO *mmi;\r
6348 \r
6349   switch (message) {\r
6350   case WM_INITDIALOG: /* message: initialize dialog box */\r
6351     /* Initialize the dialog items */\r
6352     Translate(hDlg, DLG_EditComment);\r
6353     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6354     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6355     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6356     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6357     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6358     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6359     SetWindowText(hDlg, commentTitle);\r
6360     if (editComment) {\r
6361       SetFocus(hwndText);\r
6362     } else {\r
6363       SetFocus(GetDlgItem(hDlg, IDOK));\r
6364     }\r
6365     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6366                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6367                 MAKELPARAM(FALSE, 0));\r
6368     /* Size and position the dialog */\r
6369     if (!commentDialog) {\r
6370       commentDialog = hDlg;\r
6371       flags = SWP_NOZORDER;\r
6372       GetClientRect(hDlg, &rect);\r
6373       sizeX = rect.right;\r
6374       sizeY = rect.bottom;\r
6375       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6376           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6377         WINDOWPLACEMENT wp;\r
6378         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6379         wp.length = sizeof(WINDOWPLACEMENT);\r
6380         wp.flags = 0;\r
6381         wp.showCmd = SW_SHOW;\r
6382         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6383         wp.rcNormalPosition.left = wpComment.x;\r
6384         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6385         wp.rcNormalPosition.top = wpComment.y;\r
6386         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6387         SetWindowPlacement(hDlg, &wp);\r
6388 \r
6389         GetClientRect(hDlg, &rect);\r
6390         newSizeX = rect.right;\r
6391         newSizeY = rect.bottom;\r
6392         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6393                               newSizeX, newSizeY);\r
6394         sizeX = newSizeX;\r
6395         sizeY = newSizeY;\r
6396       }\r
6397     }\r
6398     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6399     return FALSE;\r
6400 \r
6401   case WM_COMMAND: /* message: received a command */\r
6402     switch (LOWORD(wParam)) {\r
6403     case IDOK:\r
6404       if (editComment) {\r
6405         char *p, *q;\r
6406         /* Read changed options from the dialog box */\r
6407         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6408         len = GetWindowTextLength(hwndText);\r
6409         str = (char *) malloc(len + 1);\r
6410         GetWindowText(hwndText, str, len + 1);\r
6411         p = q = str;\r
6412         while (*q) {\r
6413           if (*q == '\r')\r
6414             q++;\r
6415           else\r
6416             *p++ = *q++;\r
6417         }\r
6418         *p = NULLCHAR;\r
6419         ReplaceComment(commentIndex, str);\r
6420         free(str);\r
6421       }\r
6422       CommentPopDown();\r
6423       return TRUE;\r
6424 \r
6425     case IDCANCEL:\r
6426     case OPT_CancelComment:\r
6427       CommentPopDown();\r
6428       return TRUE;\r
6429 \r
6430     case OPT_ClearComment:\r
6431       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6432       break;\r
6433 \r
6434     case OPT_EditComment:\r
6435       EditCommentEvent();\r
6436       return TRUE;\r
6437 \r
6438     default:\r
6439       break;\r
6440     }\r
6441     break;\r
6442 \r
6443   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6444         if( wParam == OPT_CommentText ) {\r
6445             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6446 \r
6447             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6448                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6449                 POINTL pt;\r
6450                 LRESULT index;\r
6451 \r
6452                 pt.x = LOWORD( lpMF->lParam );\r
6453                 pt.y = HIWORD( lpMF->lParam );\r
6454 \r
6455                 if(lpMF->msg == WM_CHAR) {\r
6456                         CHARRANGE sel;\r
6457                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6458                         index = sel.cpMin;\r
6459                 } else\r
6460                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6461 \r
6462                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6463                 len = GetWindowTextLength(hwndText);\r
6464                 str = (char *) malloc(len + 1);\r
6465                 GetWindowText(hwndText, str, len + 1);\r
6466                 ReplaceComment(commentIndex, str);\r
6467                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6468                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6469                 free(str);\r
6470 \r
6471                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6472                 lpMF->msg = WM_USER;\r
6473 \r
6474                 return TRUE;\r
6475             }\r
6476         }\r
6477         break;\r
6478 \r
6479   case WM_SIZE:\r
6480     newSizeX = LOWORD(lParam);\r
6481     newSizeY = HIWORD(lParam);\r
6482     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6483     sizeX = newSizeX;\r
6484     sizeY = newSizeY;\r
6485     break;\r
6486 \r
6487   case WM_GETMINMAXINFO:\r
6488     /* Prevent resizing window too small */\r
6489     mmi = (MINMAXINFO *) lParam;\r
6490     mmi->ptMinTrackSize.x = 100;\r
6491     mmi->ptMinTrackSize.y = 100;\r
6492     break;\r
6493   }\r
6494   return FALSE;\r
6495 }\r
6496 \r
6497 VOID\r
6498 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6499 {\r
6500   FARPROC lpProc;\r
6501   char *p, *q;\r
6502 \r
6503   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6504 \r
6505   if (str == NULL) str = "";\r
6506   p = (char *) malloc(2 * strlen(str) + 2);\r
6507   q = p;\r
6508   while (*str) {\r
6509     if (*str == '\n') *q++ = '\r';\r
6510     *q++ = *str++;\r
6511   }\r
6512   *q = NULLCHAR;\r
6513   if (commentText != NULL) free(commentText);\r
6514 \r
6515   commentIndex = index;\r
6516   commentTitle = title;\r
6517   commentText = p;\r
6518   editComment = edit;\r
6519 \r
6520   if (commentDialog) {\r
6521     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6522     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6523   } else {\r
6524     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6525     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6526                  hwndMain, (DLGPROC)lpProc);\r
6527     FreeProcInstance(lpProc);\r
6528   }\r
6529   commentUp = TRUE;\r
6530 }\r
6531 \r
6532 \r
6533 /*---------------------------------------------------------------------------*\\r
6534  *\r
6535  * Type-in move dialog functions\r
6536  * \r
6537 \*---------------------------------------------------------------------------*/\r
6538 \r
6539 LRESULT CALLBACK\r
6540 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6541 {\r
6542   char move[MSG_SIZ];\r
6543   HWND hInput;\r
6544 \r
6545   switch (message) {\r
6546   case WM_INITDIALOG:\r
6547     move[0] = (char) lParam;\r
6548     move[1] = NULLCHAR;\r
6549     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6550     Translate(hDlg, DLG_TypeInMove);\r
6551     hInput = GetDlgItem(hDlg, OPT_Move);\r
6552     SetWindowText(hInput, move);\r
6553     SetFocus(hInput);\r
6554     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6555     return FALSE;\r
6556 \r
6557   case WM_COMMAND:\r
6558     switch (LOWORD(wParam)) {\r
6559     case IDOK:\r
6560 \r
6561       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6562       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6563       TypeInDoneEvent(move);\r
6564       EndDialog(hDlg, TRUE);\r
6565       return TRUE;\r
6566     case IDCANCEL:\r
6567       EndDialog(hDlg, FALSE);\r
6568       return TRUE;\r
6569     default:\r
6570       break;\r
6571     }\r
6572     break;\r
6573   }\r
6574   return FALSE;\r
6575 }\r
6576 \r
6577 VOID\r
6578 PopUpMoveDialog(char firstchar)\r
6579 {\r
6580     FARPROC lpProc;\r
6581 \r
6582       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6583       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6584         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6585       FreeProcInstance(lpProc);\r
6586 }\r
6587 \r
6588 /*---------------------------------------------------------------------------*\\r
6589  *\r
6590  * Type-in name dialog functions\r
6591  * \r
6592 \*---------------------------------------------------------------------------*/\r
6593 \r
6594 LRESULT CALLBACK\r
6595 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6596 {\r
6597   char move[MSG_SIZ];\r
6598   HWND hInput;\r
6599 \r
6600   switch (message) {\r
6601   case WM_INITDIALOG:\r
6602     move[0] = (char) lParam;\r
6603     move[1] = NULLCHAR;\r
6604     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6605     Translate(hDlg, DLG_TypeInName);\r
6606     hInput = GetDlgItem(hDlg, OPT_Name);\r
6607     SetWindowText(hInput, move);\r
6608     SetFocus(hInput);\r
6609     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6610     return FALSE;\r
6611 \r
6612   case WM_COMMAND:\r
6613     switch (LOWORD(wParam)) {\r
6614     case IDOK:\r
6615       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6616       appData.userName = strdup(move);\r
6617       SetUserLogo();\r
6618       SetGameInfo();\r
6619       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6620         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6621         DisplayTitle(move);\r
6622       }\r
6623 \r
6624 \r
6625       EndDialog(hDlg, TRUE);\r
6626       return TRUE;\r
6627     case IDCANCEL:\r
6628       EndDialog(hDlg, FALSE);\r
6629       return TRUE;\r
6630     default:\r
6631       break;\r
6632     }\r
6633     break;\r
6634   }\r
6635   return FALSE;\r
6636 }\r
6637 \r
6638 VOID\r
6639 PopUpNameDialog(char firstchar)\r
6640 {\r
6641     FARPROC lpProc;\r
6642     \r
6643       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6644       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6645         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6646       FreeProcInstance(lpProc);\r
6647 }\r
6648 \r
6649 /*---------------------------------------------------------------------------*\\r
6650  *\r
6651  *  Error dialogs\r
6652  * \r
6653 \*---------------------------------------------------------------------------*/\r
6654 \r
6655 /* Nonmodal error box */\r
6656 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6657                              WPARAM wParam, LPARAM lParam);\r
6658 \r
6659 VOID\r
6660 ErrorPopUp(char *title, char *content)\r
6661 {\r
6662   FARPROC lpProc;\r
6663   char *p, *q;\r
6664   BOOLEAN modal = hwndMain == NULL;\r
6665 \r
6666   p = content;\r
6667   q = errorMessage;\r
6668   while (*p) {\r
6669     if (*p == '\n') {\r
6670       if (modal) {\r
6671         *q++ = ' ';\r
6672         p++;\r
6673       } else {\r
6674         *q++ = '\r';\r
6675         *q++ = *p++;\r
6676       }\r
6677     } else {\r
6678       *q++ = *p++;\r
6679     }\r
6680   }\r
6681   *q = NULLCHAR;\r
6682   strncpy(errorTitle, title, sizeof(errorTitle));\r
6683   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6684   \r
6685   if (modal) {\r
6686     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6687   } else {\r
6688     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6689     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6690                  hwndMain, (DLGPROC)lpProc);\r
6691     FreeProcInstance(lpProc);\r
6692   }\r
6693 }\r
6694 \r
6695 VOID\r
6696 ErrorPopDown()\r
6697 {\r
6698   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6699   if (errorDialog == NULL) return;\r
6700   DestroyWindow(errorDialog);\r
6701   errorDialog = NULL;\r
6702   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6703 }\r
6704 \r
6705 LRESULT CALLBACK\r
6706 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6707 {\r
6708   HANDLE hwndText;\r
6709   RECT rChild;\r
6710 \r
6711   switch (message) {\r
6712   case WM_INITDIALOG:\r
6713     GetWindowRect(hDlg, &rChild);\r
6714 \r
6715     /*\r
6716     SetWindowPos(hDlg, NULL, rChild.left,\r
6717       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6718       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6719     */\r
6720 \r
6721     /* \r
6722         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6723         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6724         and it doesn't work when you resize the dialog.\r
6725         For now, just give it a default position.\r
6726     */\r
6727     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6728     Translate(hDlg, DLG_Error);\r
6729 \r
6730     errorDialog = hDlg;\r
6731     SetWindowText(hDlg, errorTitle);\r
6732     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6733     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6734     return FALSE;\r
6735 \r
6736   case WM_COMMAND:\r
6737     switch (LOWORD(wParam)) {\r
6738     case IDOK:\r
6739     case IDCANCEL:\r
6740       if (errorDialog == hDlg) errorDialog = NULL;\r
6741       DestroyWindow(hDlg);\r
6742       return TRUE;\r
6743 \r
6744     default:\r
6745       break;\r
6746     }\r
6747     break;\r
6748   }\r
6749   return FALSE;\r
6750 }\r
6751 \r
6752 #ifdef GOTHIC\r
6753 HWND gothicDialog = NULL;\r
6754 \r
6755 LRESULT CALLBACK\r
6756 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6757 {\r
6758   HANDLE hwndText;\r
6759   RECT rChild;\r
6760   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6761 \r
6762   switch (message) {\r
6763   case WM_INITDIALOG:\r
6764     GetWindowRect(hDlg, &rChild);\r
6765 \r
6766     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6767                                                              SWP_NOZORDER);\r
6768 \r
6769     /* \r
6770         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6771         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6772         and it doesn't work when you resize the dialog.\r
6773         For now, just give it a default position.\r
6774     */\r
6775     gothicDialog = hDlg;\r
6776     SetWindowText(hDlg, errorTitle);\r
6777     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6778     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6779     return FALSE;\r
6780 \r
6781   case WM_COMMAND:\r
6782     switch (LOWORD(wParam)) {\r
6783     case IDOK:\r
6784     case IDCANCEL:\r
6785       if (errorDialog == hDlg) errorDialog = NULL;\r
6786       DestroyWindow(hDlg);\r
6787       return TRUE;\r
6788 \r
6789     default:\r
6790       break;\r
6791     }\r
6792     break;\r
6793   }\r
6794   return FALSE;\r
6795 }\r
6796 \r
6797 VOID\r
6798 GothicPopUp(char *title, VariantClass variant)\r
6799 {\r
6800   FARPROC lpProc;\r
6801   static char *lastTitle;\r
6802 \r
6803   strncpy(errorTitle, title, sizeof(errorTitle));\r
6804   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6805 \r
6806   if(lastTitle != title && gothicDialog != NULL) {\r
6807     DestroyWindow(gothicDialog);\r
6808     gothicDialog = NULL;\r
6809   }\r
6810   if(variant != VariantNormal && gothicDialog == NULL) {\r
6811     title = lastTitle;\r
6812     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6813     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6814                  hwndMain, (DLGPROC)lpProc);\r
6815     FreeProcInstance(lpProc);\r
6816   }\r
6817 }\r
6818 #endif\r
6819 \r
6820 /*---------------------------------------------------------------------------*\\r
6821  *\r
6822  *  Ics Interaction console functions\r
6823  *\r
6824 \*---------------------------------------------------------------------------*/\r
6825 \r
6826 #define HISTORY_SIZE 64\r
6827 static char *history[HISTORY_SIZE];\r
6828 int histIn = 0, histP = 0;\r
6829 \r
6830 VOID\r
6831 SaveInHistory(char *cmd)\r
6832 {\r
6833   if (history[histIn] != NULL) {\r
6834     free(history[histIn]);\r
6835     history[histIn] = NULL;\r
6836   }\r
6837   if (*cmd == NULLCHAR) return;\r
6838   history[histIn] = StrSave(cmd);\r
6839   histIn = (histIn + 1) % HISTORY_SIZE;\r
6840   if (history[histIn] != NULL) {\r
6841     free(history[histIn]);\r
6842     history[histIn] = NULL;\r
6843   }\r
6844   histP = histIn;\r
6845 }\r
6846 \r
6847 char *\r
6848 PrevInHistory(char *cmd)\r
6849 {\r
6850   int newhp;\r
6851   if (histP == histIn) {\r
6852     if (history[histIn] != NULL) free(history[histIn]);\r
6853     history[histIn] = StrSave(cmd);\r
6854   }\r
6855   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6856   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6857   histP = newhp;\r
6858   return history[histP];\r
6859 }\r
6860 \r
6861 char *\r
6862 NextInHistory()\r
6863 {\r
6864   if (histP == histIn) return NULL;\r
6865   histP = (histP + 1) % HISTORY_SIZE;\r
6866   return history[histP];   \r
6867 }\r
6868 \r
6869 HMENU\r
6870 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6871 {\r
6872   HMENU hmenu, h;\r
6873   int i = 0;\r
6874   hmenu = LoadMenu(hInst, "TextMenu");\r
6875   h = GetSubMenu(hmenu, 0);\r
6876   while (e->item) {\r
6877     if (strcmp(e->item, "-") == 0) {\r
6878       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6879     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6880       int flags = MF_STRING, j = 0;\r
6881       if (e->item[0] == '|') {\r
6882         flags |= MF_MENUBARBREAK;\r
6883         j++;\r
6884       }\r
6885       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6886       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6887     }\r
6888     e++;\r
6889     i++;\r
6890   } \r
6891   return hmenu;\r
6892 }\r
6893 \r
6894 WNDPROC consoleTextWindowProc;\r
6895 \r
6896 void\r
6897 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6898 {\r
6899   char buf[MSG_SIZ], name[MSG_SIZ];\r
6900   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6901   CHARRANGE sel;\r
6902 \r
6903   if (!getname) {\r
6904     SetWindowText(hInput, command);\r
6905     if (immediate) {\r
6906       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6907     } else {\r
6908       sel.cpMin = 999999;\r
6909       sel.cpMax = 999999;\r
6910       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6911       SetFocus(hInput);\r
6912     }\r
6913     return;\r
6914   }    \r
6915   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6916   if (sel.cpMin == sel.cpMax) {\r
6917     /* Expand to surrounding word */\r
6918     TEXTRANGE tr;\r
6919     do {\r
6920       tr.chrg.cpMax = sel.cpMin;\r
6921       tr.chrg.cpMin = --sel.cpMin;\r
6922       if (sel.cpMin < 0) break;\r
6923       tr.lpstrText = name;\r
6924       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6925     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6926     sel.cpMin++;\r
6927 \r
6928     do {\r
6929       tr.chrg.cpMin = sel.cpMax;\r
6930       tr.chrg.cpMax = ++sel.cpMax;\r
6931       tr.lpstrText = name;\r
6932       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6933     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6934     sel.cpMax--;\r
6935 \r
6936     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6937       MessageBeep(MB_ICONEXCLAMATION);\r
6938       return;\r
6939     }\r
6940     tr.chrg = sel;\r
6941     tr.lpstrText = name;\r
6942     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6943   } else {\r
6944     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6945       MessageBeep(MB_ICONEXCLAMATION);\r
6946       return;\r
6947     }\r
6948     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6949   }\r
6950   if (immediate) {\r
6951     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6952     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6953     SetWindowText(hInput, buf);\r
6954     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6955   } else {\r
6956     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6957       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6958     SetWindowText(hInput, buf);\r
6959     sel.cpMin = 999999;\r
6960     sel.cpMax = 999999;\r
6961     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6962     SetFocus(hInput);\r
6963   }\r
6964 }\r
6965 \r
6966 LRESULT CALLBACK \r
6967 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6968 {\r
6969   HWND hInput;\r
6970   CHARRANGE sel;\r
6971 \r
6972   switch (message) {\r
6973   case WM_KEYDOWN:\r
6974     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6975     if(wParam=='R') return 0;\r
6976     switch (wParam) {\r
6977     case VK_PRIOR:\r
6978       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6979       return 0;\r
6980     case VK_NEXT:\r
6981       sel.cpMin = 999999;\r
6982       sel.cpMax = 999999;\r
6983       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6984       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6985       return 0;\r
6986     }\r
6987     break;\r
6988   case WM_CHAR:\r
6989    if(wParam != '\022') {\r
6990     if (wParam == '\t') {\r
6991       if (GetKeyState(VK_SHIFT) < 0) {\r
6992         /* shifted */\r
6993         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6994         if (buttonDesc[0].hwnd) {\r
6995           SetFocus(buttonDesc[0].hwnd);\r
6996         } else {\r
6997           SetFocus(hwndMain);\r
6998         }\r
6999       } else {\r
7000         /* unshifted */\r
7001         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7002       }\r
7003     } else {\r
7004       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7005       JAWS_DELETE( SetFocus(hInput); )\r
7006       SendMessage(hInput, message, wParam, lParam);\r
7007     }\r
7008     return 0;\r
7009    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7010    lParam = -1;\r
7011   case WM_RBUTTONDOWN:\r
7012     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7013       /* Move selection here if it was empty */\r
7014       POINT pt;\r
7015       pt.x = LOWORD(lParam);\r
7016       pt.y = HIWORD(lParam);\r
7017       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7018       if (sel.cpMin == sel.cpMax) {\r
7019         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7020         sel.cpMax = sel.cpMin;\r
7021         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7022       }\r
7023       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7024 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7025       POINT pt;\r
7026       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7027       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7028       if (sel.cpMin == sel.cpMax) {\r
7029         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7030         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7031       }\r
7032       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7033         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7034       }\r
7035       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7036       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7037       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7038       MenuPopup(hwnd, pt, hmenu, -1);\r
7039 }\r
7040     }\r
7041     return 0;\r
7042   case WM_RBUTTONUP:\r
7043     if (GetKeyState(VK_SHIFT) & ~1) {\r
7044       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7045         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7046     }\r
7047     return 0;\r
7048   case WM_PASTE:\r
7049     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7050     SetFocus(hInput);\r
7051     return SendMessage(hInput, message, wParam, lParam);\r
7052   case WM_MBUTTONDOWN:\r
7053     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7054   case WM_COMMAND:\r
7055     switch (LOWORD(wParam)) {\r
7056     case IDM_QuickPaste:\r
7057       {\r
7058         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7059         if (sel.cpMin == sel.cpMax) {\r
7060           MessageBeep(MB_ICONEXCLAMATION);\r
7061           return 0;\r
7062         }\r
7063         SendMessage(hwnd, WM_COPY, 0, 0);\r
7064         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7065         SendMessage(hInput, WM_PASTE, 0, 0);\r
7066         SetFocus(hInput);\r
7067         return 0;\r
7068       }\r
7069     case IDM_Cut:\r
7070       SendMessage(hwnd, WM_CUT, 0, 0);\r
7071       return 0;\r
7072     case IDM_Paste:\r
7073       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7074       return 0;\r
7075     case IDM_Copy:\r
7076       SendMessage(hwnd, WM_COPY, 0, 0);\r
7077       return 0;\r
7078     default:\r
7079       {\r
7080         int i = LOWORD(wParam) - IDM_CommandX;\r
7081         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7082             icsTextMenuEntry[i].command != NULL) {\r
7083           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7084                    icsTextMenuEntry[i].getname,\r
7085                    icsTextMenuEntry[i].immediate);\r
7086           return 0;\r
7087         }\r
7088       }\r
7089       break;\r
7090     }\r
7091     break;\r
7092   }\r
7093   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7094 }\r
7095 \r
7096 WNDPROC consoleInputWindowProc;\r
7097 \r
7098 LRESULT CALLBACK\r
7099 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7100 {\r
7101   char buf[MSG_SIZ];\r
7102   char *p;\r
7103   static BOOL sendNextChar = FALSE;\r
7104   static BOOL quoteNextChar = FALSE;\r
7105   InputSource *is = consoleInputSource;\r
7106   CHARFORMAT cf;\r
7107   CHARRANGE sel;\r
7108 \r
7109   switch (message) {\r
7110   case WM_CHAR:\r
7111     if (!appData.localLineEditing || sendNextChar) {\r
7112       is->buf[0] = (CHAR) wParam;\r
7113       is->count = 1;\r
7114       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7115       sendNextChar = FALSE;\r
7116       return 0;\r
7117     }\r
7118     if (quoteNextChar) {\r
7119       buf[0] = (char) wParam;\r
7120       buf[1] = NULLCHAR;\r
7121       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7122       quoteNextChar = FALSE;\r
7123       return 0;\r
7124     }\r
7125     switch (wParam) {\r
7126     case '\r':   /* Enter key */\r
7127       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7128       if (consoleEcho) SaveInHistory(is->buf);\r
7129       is->buf[is->count++] = '\n';\r
7130       is->buf[is->count] = NULLCHAR;\r
7131       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7132       if (consoleEcho) {\r
7133         ConsoleOutput(is->buf, is->count, TRUE);\r
7134       } else if (appData.localLineEditing) {\r
7135         ConsoleOutput("\n", 1, TRUE);\r
7136       }\r
7137       /* fall thru */\r
7138     case '\033': /* Escape key */\r
7139       SetWindowText(hwnd, "");\r
7140       cf.cbSize = sizeof(CHARFORMAT);\r
7141       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7142       if (consoleEcho) {\r
7143         cf.crTextColor = textAttribs[ColorNormal].color;\r
7144       } else {\r
7145         cf.crTextColor = COLOR_ECHOOFF;\r
7146       }\r
7147       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7148       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7149       return 0;\r
7150     case '\t':   /* Tab key */\r
7151       if (GetKeyState(VK_SHIFT) < 0) {\r
7152         /* shifted */\r
7153         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7154       } else {\r
7155         /* unshifted */\r
7156         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7157         if (buttonDesc[0].hwnd) {\r
7158           SetFocus(buttonDesc[0].hwnd);\r
7159         } else {\r
7160           SetFocus(hwndMain);\r
7161         }\r
7162       }\r
7163       return 0;\r
7164     case '\023': /* Ctrl+S */\r
7165       sendNextChar = TRUE;\r
7166       return 0;\r
7167     case '\021': /* Ctrl+Q */\r
7168       quoteNextChar = TRUE;\r
7169       return 0;\r
7170     JAWS_REPLAY\r
7171     default:\r
7172       break;\r
7173     }\r
7174     break;\r
7175   case WM_KEYDOWN:\r
7176     switch (wParam) {\r
7177     case VK_UP:\r
7178       GetWindowText(hwnd, buf, MSG_SIZ);\r
7179       p = PrevInHistory(buf);\r
7180       if (p != NULL) {\r
7181         SetWindowText(hwnd, p);\r
7182         sel.cpMin = 999999;\r
7183         sel.cpMax = 999999;\r
7184         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7185         return 0;\r
7186       }\r
7187       break;\r
7188     case VK_DOWN:\r
7189       p = NextInHistory();\r
7190       if (p != NULL) {\r
7191         SetWindowText(hwnd, p);\r
7192         sel.cpMin = 999999;\r
7193         sel.cpMax = 999999;\r
7194         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7195         return 0;\r
7196       }\r
7197       break;\r
7198     case VK_HOME:\r
7199     case VK_END:\r
7200       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7201       /* fall thru */\r
7202     case VK_PRIOR:\r
7203     case VK_NEXT:\r
7204       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7205       return 0;\r
7206     }\r
7207     break;\r
7208   case WM_MBUTTONDOWN:\r
7209     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7210       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7211     break;\r
7212   case WM_RBUTTONUP:\r
7213     if (GetKeyState(VK_SHIFT) & ~1) {\r
7214       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7215         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7216     } else {\r
7217       POINT pt;\r
7218       HMENU hmenu;\r
7219       hmenu = LoadMenu(hInst, "InputMenu");\r
7220       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7221       if (sel.cpMin == sel.cpMax) {\r
7222         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7223         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7224       }\r
7225       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7226         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7227       }\r
7228       pt.x = LOWORD(lParam);\r
7229       pt.y = HIWORD(lParam);\r
7230       MenuPopup(hwnd, pt, hmenu, -1);\r
7231     }\r
7232     return 0;\r
7233   case WM_COMMAND:\r
7234     switch (LOWORD(wParam)) { \r
7235     case IDM_Undo:\r
7236       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7237       return 0;\r
7238     case IDM_SelectAll:\r
7239       sel.cpMin = 0;\r
7240       sel.cpMax = -1; /*999999?*/\r
7241       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7242       return 0;\r
7243     case IDM_Cut:\r
7244       SendMessage(hwnd, WM_CUT, 0, 0);\r
7245       return 0;\r
7246     case IDM_Paste:\r
7247       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7248       return 0;\r
7249     case IDM_Copy:\r
7250       SendMessage(hwnd, WM_COPY, 0, 0);\r
7251       return 0;\r
7252     }\r
7253     break;\r
7254   }\r
7255   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7256 }\r
7257 \r
7258 #define CO_MAX  100000\r
7259 #define CO_TRIM   1000\r
7260 \r
7261 LRESULT CALLBACK\r
7262 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7263 {\r
7264   static SnapData sd;\r
7265   HWND hText, hInput;\r
7266   RECT rect;\r
7267   static int sizeX, sizeY;\r
7268   int newSizeX, newSizeY;\r
7269   MINMAXINFO *mmi;\r
7270   WORD wMask;\r
7271 \r
7272   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7273   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7274 \r
7275   switch (message) {\r
7276   case WM_NOTIFY:\r
7277     if (((NMHDR*)lParam)->code == EN_LINK)\r
7278     {\r
7279       ENLINK *pLink = (ENLINK*)lParam;\r
7280       if (pLink->msg == WM_LBUTTONUP)\r
7281       {\r
7282         TEXTRANGE tr;\r
7283 \r
7284         tr.chrg = pLink->chrg;\r
7285         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7286         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7287         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7288         free(tr.lpstrText);\r
7289       }\r
7290     }\r
7291     break;\r
7292   case WM_INITDIALOG: /* message: initialize dialog box */\r
7293     hwndConsole = hDlg;\r
7294     SetFocus(hInput);\r
7295     consoleTextWindowProc = (WNDPROC)\r
7296       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7297     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7298     consoleInputWindowProc = (WNDPROC)\r
7299       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7300     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7301     Colorize(ColorNormal, TRUE);\r
7302     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7303     ChangedConsoleFont();\r
7304     GetClientRect(hDlg, &rect);\r
7305     sizeX = rect.right;\r
7306     sizeY = rect.bottom;\r
7307     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7308         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7309       WINDOWPLACEMENT wp;\r
7310       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7311       wp.length = sizeof(WINDOWPLACEMENT);\r
7312       wp.flags = 0;\r
7313       wp.showCmd = SW_SHOW;\r
7314       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7315       wp.rcNormalPosition.left = wpConsole.x;\r
7316       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7317       wp.rcNormalPosition.top = wpConsole.y;\r
7318       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7319       SetWindowPlacement(hDlg, &wp);\r
7320     }\r
7321 \r
7322    // [HGM] Chessknight's change 2004-07-13\r
7323    else { /* Determine Defaults */\r
7324        WINDOWPLACEMENT wp;\r
7325        wpConsole.x = wpMain.width + 1;\r
7326        wpConsole.y = wpMain.y;\r
7327        wpConsole.width = screenWidth -  wpMain.width;\r
7328        wpConsole.height = wpMain.height;\r
7329        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7330        wp.length = sizeof(WINDOWPLACEMENT);\r
7331        wp.flags = 0;\r
7332        wp.showCmd = SW_SHOW;\r
7333        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7334        wp.rcNormalPosition.left = wpConsole.x;\r
7335        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7336        wp.rcNormalPosition.top = wpConsole.y;\r
7337        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7338        SetWindowPlacement(hDlg, &wp);\r
7339     }\r
7340 \r
7341    // Allow hText to highlight URLs and send notifications on them\r
7342    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7343    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7344    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7345    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7346 \r
7347     return FALSE;\r
7348 \r
7349   case WM_SETFOCUS:\r
7350     SetFocus(hInput);\r
7351     return 0;\r
7352 \r
7353   case WM_CLOSE:\r
7354     ExitEvent(0);\r
7355     /* not reached */\r
7356     break;\r
7357 \r
7358   case WM_SIZE:\r
7359     if (IsIconic(hDlg)) break;\r
7360     newSizeX = LOWORD(lParam);\r
7361     newSizeY = HIWORD(lParam);\r
7362     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7363       RECT rectText, rectInput;\r
7364       POINT pt;\r
7365       int newTextHeight, newTextWidth;\r
7366       GetWindowRect(hText, &rectText);\r
7367       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7368       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7369       if (newTextHeight < 0) {\r
7370         newSizeY += -newTextHeight;\r
7371         newTextHeight = 0;\r
7372       }\r
7373       SetWindowPos(hText, NULL, 0, 0,\r
7374         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7375       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7376       pt.x = rectInput.left;\r
7377       pt.y = rectInput.top + newSizeY - sizeY;\r
7378       ScreenToClient(hDlg, &pt);\r
7379       SetWindowPos(hInput, NULL, \r
7380         pt.x, pt.y, /* needs client coords */   \r
7381         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7382         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7383     }\r
7384     sizeX = newSizeX;\r
7385     sizeY = newSizeY;\r
7386     break;\r
7387 \r
7388   case WM_GETMINMAXINFO:\r
7389     /* Prevent resizing window too small */\r
7390     mmi = (MINMAXINFO *) lParam;\r
7391     mmi->ptMinTrackSize.x = 100;\r
7392     mmi->ptMinTrackSize.y = 100;\r
7393     break;\r
7394 \r
7395   /* [AS] Snapping */\r
7396   case WM_ENTERSIZEMOVE:\r
7397     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7398 \r
7399   case WM_SIZING:\r
7400     return OnSizing( &sd, hDlg, wParam, lParam );\r
7401 \r
7402   case WM_MOVING:\r
7403     return OnMoving( &sd, hDlg, wParam, lParam );\r
7404 \r
7405   case WM_EXITSIZEMOVE:\r
7406         UpdateICSWidth(hText);\r
7407     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7408   }\r
7409 \r
7410   return DefWindowProc(hDlg, message, wParam, lParam);\r
7411 }\r
7412 \r
7413 \r
7414 VOID\r
7415 ConsoleCreate()\r
7416 {\r
7417   HWND hCons;\r
7418   if (hwndConsole) return;\r
7419   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7420   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7421 }\r
7422 \r
7423 \r
7424 VOID\r
7425 ConsoleOutput(char* data, int length, int forceVisible)\r
7426 {\r
7427   HWND hText;\r
7428   int trim, exlen;\r
7429   char *p, *q;\r
7430   char buf[CO_MAX+1];\r
7431   POINT pEnd;\r
7432   RECT rect;\r
7433   static int delayLF = 0;\r
7434   CHARRANGE savesel, sel;\r
7435 \r
7436   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7437   p = data;\r
7438   q = buf;\r
7439   if (delayLF) {\r
7440     *q++ = '\r';\r
7441     *q++ = '\n';\r
7442     delayLF = 0;\r
7443   }\r
7444   while (length--) {\r
7445     if (*p == '\n') {\r
7446       if (*++p) {\r
7447         *q++ = '\r';\r
7448         *q++ = '\n';\r
7449       } else {\r
7450         delayLF = 1;\r
7451       }\r
7452     } else if (*p == '\007') {\r
7453        MyPlaySound(&sounds[(int)SoundBell]);\r
7454        p++;\r
7455     } else {\r
7456       *q++ = *p++;\r
7457     }\r
7458   }\r
7459   *q = NULLCHAR;\r
7460   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7461   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7462   /* Save current selection */\r
7463   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7464   exlen = GetWindowTextLength(hText);\r
7465   /* Find out whether current end of text is visible */\r
7466   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7467   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7468   /* Trim existing text if it's too long */\r
7469   if (exlen + (q - buf) > CO_MAX) {\r
7470     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7471     sel.cpMin = 0;\r
7472     sel.cpMax = trim;\r
7473     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7474     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7475     exlen -= trim;\r
7476     savesel.cpMin -= trim;\r
7477     savesel.cpMax -= trim;\r
7478     if (exlen < 0) exlen = 0;\r
7479     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7480     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7481   }\r
7482   /* Append the new text */\r
7483   sel.cpMin = exlen;\r
7484   sel.cpMax = exlen;\r
7485   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7486   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7487   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7488   if (forceVisible || exlen == 0 ||\r
7489       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7490        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7491     /* Scroll to make new end of text visible if old end of text\r
7492        was visible or new text is an echo of user typein */\r
7493     sel.cpMin = 9999999;\r
7494     sel.cpMax = 9999999;\r
7495     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7496     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7497     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7498     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7499   }\r
7500   if (savesel.cpMax == exlen || forceVisible) {\r
7501     /* Move insert point to new end of text if it was at the old\r
7502        end of text or if the new text is an echo of user typein */\r
7503     sel.cpMin = 9999999;\r
7504     sel.cpMax = 9999999;\r
7505     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7506   } else {\r
7507     /* Restore previous selection */\r
7508     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7509   }\r
7510   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7511 }\r
7512 \r
7513 /*---------*/\r
7514 \r
7515 \r
7516 void\r
7517 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7518 {\r
7519   char buf[100];\r
7520   char *str;\r
7521   COLORREF oldFg, oldBg;\r
7522   HFONT oldFont;\r
7523   RECT rect;\r
7524 \r
7525   if(copyNumber > 1)\r
7526     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7527 \r
7528   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7529   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7530   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7531 \r
7532   rect.left = x;\r
7533   rect.right = x + squareSize;\r
7534   rect.top  = y;\r
7535   rect.bottom = y + squareSize;\r
7536   str = buf;\r
7537 \r
7538   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7539                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7540              y, ETO_CLIPPED|ETO_OPAQUE,\r
7541              &rect, str, strlen(str), NULL);\r
7542 \r
7543   (void) SetTextColor(hdc, oldFg);\r
7544   (void) SetBkColor(hdc, oldBg);\r
7545   (void) SelectObject(hdc, oldFont);\r
7546 }\r
7547 \r
7548 void\r
7549 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7550               RECT *rect, char *color, char *flagFell)\r
7551 {\r
7552   char buf[100];\r
7553   char *str;\r
7554   COLORREF oldFg, oldBg;\r
7555   HFONT oldFont;\r
7556 \r
7557   if (twoBoards && partnerUp) return;\r
7558   if (appData.clockMode) {\r
7559     if (tinyLayout)\r
7560       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7561     else\r
7562       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7563     str = buf;\r
7564   } else {\r
7565     str = color;\r
7566   }\r
7567 \r
7568   if (highlight) {\r
7569     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7570     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7571   } else {\r
7572     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7573     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7574   }\r
7575   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7576 \r
7577   JAWS_SILENCE\r
7578 \r
7579   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7580              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7581              rect, str, strlen(str), NULL);\r
7582   if(logoHeight > 0 && appData.clockMode) {\r
7583       RECT r;\r
7584       str += strlen(color)+2;\r
7585       r.top = rect->top + logoHeight/2;\r
7586       r.left = rect->left;\r
7587       r.right = rect->right;\r
7588       r.bottom = rect->bottom;\r
7589       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7590                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7591                  &r, str, strlen(str), NULL);\r
7592   }\r
7593   (void) SetTextColor(hdc, oldFg);\r
7594   (void) SetBkColor(hdc, oldBg);\r
7595   (void) SelectObject(hdc, oldFont);\r
7596 }\r
7597 \r
7598 \r
7599 int\r
7600 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7601            OVERLAPPED *ovl)\r
7602 {\r
7603   int ok, err;\r
7604 \r
7605   /* [AS]  */\r
7606   if( count <= 0 ) {\r
7607     if (appData.debugMode) {\r
7608       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7609     }\r
7610 \r
7611     return ERROR_INVALID_USER_BUFFER;\r
7612   }\r
7613 \r
7614   ResetEvent(ovl->hEvent);\r
7615   ovl->Offset = ovl->OffsetHigh = 0;\r
7616   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7617   if (ok) {\r
7618     err = NO_ERROR;\r
7619   } else {\r
7620     err = GetLastError();\r
7621     if (err == ERROR_IO_PENDING) {\r
7622       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7623       if (ok)\r
7624         err = NO_ERROR;\r
7625       else\r
7626         err = GetLastError();\r
7627     }\r
7628   }\r
7629   return err;\r
7630 }\r
7631 \r
7632 int\r
7633 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7634             OVERLAPPED *ovl)\r
7635 {\r
7636   int ok, err;\r
7637 \r
7638   ResetEvent(ovl->hEvent);\r
7639   ovl->Offset = ovl->OffsetHigh = 0;\r
7640   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7641   if (ok) {\r
7642     err = NO_ERROR;\r
7643   } else {\r
7644     err = GetLastError();\r
7645     if (err == ERROR_IO_PENDING) {\r
7646       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7647       if (ok)\r
7648         err = NO_ERROR;\r
7649       else\r
7650         err = GetLastError();\r
7651     }\r
7652   }\r
7653   return err;\r
7654 }\r
7655 \r
7656 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7657 void CheckForInputBufferFull( InputSource * is )\r
7658 {\r
7659     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7660         /* Look for end of line */\r
7661         char * p = is->buf;\r
7662         \r
7663         while( p < is->next && *p != '\n' ) {\r
7664             p++;\r
7665         }\r
7666 \r
7667         if( p >= is->next ) {\r
7668             if (appData.debugMode) {\r
7669                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7670             }\r
7671 \r
7672             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7673             is->count = (DWORD) -1;\r
7674             is->next = is->buf;\r
7675         }\r
7676     }\r
7677 }\r
7678 \r
7679 DWORD\r
7680 InputThread(LPVOID arg)\r
7681 {\r
7682   InputSource *is;\r
7683   OVERLAPPED ovl;\r
7684 \r
7685   is = (InputSource *) arg;\r
7686   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7687   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7688   while (is->hThread != NULL) {\r
7689     is->error = DoReadFile(is->hFile, is->next,\r
7690                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7691                            &is->count, &ovl);\r
7692     if (is->error == NO_ERROR) {\r
7693       is->next += is->count;\r
7694     } else {\r
7695       if (is->error == ERROR_BROKEN_PIPE) {\r
7696         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7697         is->count = 0;\r
7698       } else {\r
7699         is->count = (DWORD) -1;\r
7700         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7701         break; \r
7702       }\r
7703     }\r
7704 \r
7705     CheckForInputBufferFull( is );\r
7706 \r
7707     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7708 \r
7709     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7710 \r
7711     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7712   }\r
7713 \r
7714   CloseHandle(ovl.hEvent);\r
7715   CloseHandle(is->hFile);\r
7716 \r
7717   if (appData.debugMode) {\r
7718     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7719   }\r
7720 \r
7721   return 0;\r
7722 }\r
7723 \r
7724 \r
7725 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7726 DWORD\r
7727 NonOvlInputThread(LPVOID arg)\r
7728 {\r
7729   InputSource *is;\r
7730   char *p, *q;\r
7731   int i;\r
7732   char prev;\r
7733 \r
7734   is = (InputSource *) arg;\r
7735   while (is->hThread != NULL) {\r
7736     is->error = ReadFile(is->hFile, is->next,\r
7737                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7738                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7739     if (is->error == NO_ERROR) {\r
7740       /* Change CRLF to LF */\r
7741       if (is->next > is->buf) {\r
7742         p = is->next - 1;\r
7743         i = is->count + 1;\r
7744       } else {\r
7745         p = is->next;\r
7746         i = is->count;\r
7747       }\r
7748       q = p;\r
7749       prev = NULLCHAR;\r
7750       while (i > 0) {\r
7751         if (prev == '\r' && *p == '\n') {\r
7752           *(q-1) = '\n';\r
7753           is->count--;\r
7754         } else { \r
7755           *q++ = *p;\r
7756         }\r
7757         prev = *p++;\r
7758         i--;\r
7759       }\r
7760       *q = NULLCHAR;\r
7761       is->next = q;\r
7762     } else {\r
7763       if (is->error == ERROR_BROKEN_PIPE) {\r
7764         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7765         is->count = 0; \r
7766       } else {\r
7767         is->count = (DWORD) -1;\r
7768       }\r
7769     }\r
7770 \r
7771     CheckForInputBufferFull( is );\r
7772 \r
7773     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7774 \r
7775     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7776 \r
7777     if (is->count < 0) break;  /* Quit on error */\r
7778   }\r
7779   CloseHandle(is->hFile);\r
7780   return 0;\r
7781 }\r
7782 \r
7783 DWORD\r
7784 SocketInputThread(LPVOID arg)\r
7785 {\r
7786   InputSource *is;\r
7787 \r
7788   is = (InputSource *) arg;\r
7789   while (is->hThread != NULL) {\r
7790     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7791     if ((int)is->count == SOCKET_ERROR) {\r
7792       is->count = (DWORD) -1;\r
7793       is->error = WSAGetLastError();\r
7794     } else {\r
7795       is->error = NO_ERROR;\r
7796       is->next += is->count;\r
7797       if (is->count == 0 && is->second == is) {\r
7798         /* End of file on stderr; quit with no message */\r
7799         break;\r
7800       }\r
7801     }\r
7802     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7803 \r
7804     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7805 \r
7806     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7807   }\r
7808   return 0;\r
7809 }\r
7810 \r
7811 VOID\r
7812 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7813 {\r
7814   InputSource *is;\r
7815 \r
7816   is = (InputSource *) lParam;\r
7817   if (is->lineByLine) {\r
7818     /* Feed in lines one by one */\r
7819     char *p = is->buf;\r
7820     char *q = p;\r
7821     while (q < is->next) {\r
7822       if (*q++ == '\n') {\r
7823         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7824         p = q;\r
7825       }\r
7826     }\r
7827     \r
7828     /* Move any partial line to the start of the buffer */\r
7829     q = is->buf;\r
7830     while (p < is->next) {\r
7831       *q++ = *p++;\r
7832     }\r
7833     is->next = q;\r
7834 \r
7835     if (is->error != NO_ERROR || is->count == 0) {\r
7836       /* Notify backend of the error.  Note: If there was a partial\r
7837          line at the end, it is not flushed through. */\r
7838       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7839     }\r
7840   } else {\r
7841     /* Feed in the whole chunk of input at once */\r
7842     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7843     is->next = is->buf;\r
7844   }\r
7845 }\r
7846 \r
7847 /*---------------------------------------------------------------------------*\\r
7848  *\r
7849  *  Menu enables. Used when setting various modes.\r
7850  *\r
7851 \*---------------------------------------------------------------------------*/\r
7852 \r
7853 typedef struct {\r
7854   int item;\r
7855   int flags;\r
7856 } Enables;\r
7857 \r
7858 VOID\r
7859 GreyRevert(Boolean grey)\r
7860 { // [HGM] vari: for retracting variations in local mode\r
7861   HMENU hmenu = GetMenu(hwndMain);\r
7862   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7863   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7864 }\r
7865 \r
7866 VOID\r
7867 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7868 {\r
7869   while (enab->item > 0) {\r
7870     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7871     enab++;\r
7872   }\r
7873 }\r
7874 \r
7875 Enables gnuEnables[] = {\r
7876   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7889 \r
7890   // Needed to switch from ncp to GNU mode on Engine Load\r
7891   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7892   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7893   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7894   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7895   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7905   { -1, -1 }\r
7906 };\r
7907 \r
7908 Enables icsEnables[] = {\r
7909   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7929   { -1, -1 }\r
7930 };\r
7931 \r
7932 #if ZIPPY\r
7933 Enables zippyEnables[] = {\r
7934   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7938   { -1, -1 }\r
7939 };\r
7940 #endif\r
7941 \r
7942 Enables ncpEnables[] = {\r
7943   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7952   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7953   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7965   { -1, -1 }\r
7966 };\r
7967 \r
7968 Enables trainingOnEnables[] = {\r
7969   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7978   { -1, -1 }\r
7979 };\r
7980 \r
7981 Enables trainingOffEnables[] = {\r
7982   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7991   { -1, -1 }\r
7992 };\r
7993 \r
7994 /* These modify either ncpEnables or gnuEnables */\r
7995 Enables cmailEnables[] = {\r
7996   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7998   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7999   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8001   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8003   { -1, -1 }\r
8004 };\r
8005 \r
8006 Enables machineThinkingEnables[] = {\r
8007   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8020 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8023   { -1, -1 }\r
8024 };\r
8025 \r
8026 Enables userThinkingEnables[] = {\r
8027   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8029   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8031   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8040 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8043   { -1, -1 }\r
8044 };\r
8045 \r
8046 /*---------------------------------------------------------------------------*\\r
8047  *\r
8048  *  Front-end interface functions exported by XBoard.\r
8049  *  Functions appear in same order as prototypes in frontend.h.\r
8050  * \r
8051 \*---------------------------------------------------------------------------*/\r
8052 VOID\r
8053 CheckMark(UINT item, int state)\r
8054 {\r
8055     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8056 }\r
8057 \r
8058 VOID\r
8059 ModeHighlight()\r
8060 {\r
8061   static UINT prevChecked = 0;\r
8062   static int prevPausing = 0;\r
8063   UINT nowChecked;\r
8064 \r
8065   if (pausing != prevPausing) {\r
8066     prevPausing = pausing;\r
8067     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8068                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8069     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8070   }\r
8071 \r
8072   switch (gameMode) {\r
8073   case BeginningOfGame:\r
8074     if (appData.icsActive)\r
8075       nowChecked = IDM_IcsClient;\r
8076     else if (appData.noChessProgram)\r
8077       nowChecked = IDM_EditGame;\r
8078     else\r
8079       nowChecked = IDM_MachineBlack;\r
8080     break;\r
8081   case MachinePlaysBlack:\r
8082     nowChecked = IDM_MachineBlack;\r
8083     break;\r
8084   case MachinePlaysWhite:\r
8085     nowChecked = IDM_MachineWhite;\r
8086     break;\r
8087   case TwoMachinesPlay:\r
8088     nowChecked = IDM_TwoMachines;\r
8089     break;\r
8090   case AnalyzeMode:\r
8091     nowChecked = IDM_AnalysisMode;\r
8092     break;\r
8093   case AnalyzeFile:\r
8094     nowChecked = IDM_AnalyzeFile;\r
8095     break;\r
8096   case EditGame:\r
8097     nowChecked = IDM_EditGame;\r
8098     break;\r
8099   case PlayFromGameFile:\r
8100     nowChecked = IDM_LoadGame;\r
8101     break;\r
8102   case EditPosition:\r
8103     nowChecked = IDM_EditPosition;\r
8104     break;\r
8105   case Training:\r
8106     nowChecked = IDM_Training;\r
8107     break;\r
8108   case IcsPlayingWhite:\r
8109   case IcsPlayingBlack:\r
8110   case IcsObserving:\r
8111   case IcsIdle:\r
8112     nowChecked = IDM_IcsClient;\r
8113     break;\r
8114   default:\r
8115   case EndOfGame:\r
8116     nowChecked = 0;\r
8117     break;\r
8118   }\r
8119   CheckMark(prevChecked, MF_UNCHECKED);\r
8120   CheckMark(nowChecked, MF_CHECKED);\r
8121   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8122 \r
8123   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8124     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8125                           MF_BYCOMMAND|MF_ENABLED);\r
8126   } else {\r
8127     (void) EnableMenuItem(GetMenu(hwndMain), \r
8128                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8129   }\r
8130 \r
8131   prevChecked = nowChecked;\r
8132 \r
8133   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8134   if (appData.icsActive) {\r
8135        if (appData.icsEngineAnalyze) {\r
8136                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8137        } else {\r
8138                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8139        }\r
8140   }\r
8141   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8142 }\r
8143 \r
8144 VOID\r
8145 SetICSMode()\r
8146 {\r
8147   HMENU hmenu = GetMenu(hwndMain);\r
8148   SetMenuEnables(hmenu, icsEnables);\r
8149   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8150     MF_BYCOMMAND|MF_ENABLED);\r
8151 #if ZIPPY\r
8152   if (appData.zippyPlay) {\r
8153     SetMenuEnables(hmenu, zippyEnables);\r
8154     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8155          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8156           MF_BYCOMMAND|MF_ENABLED);\r
8157   }\r
8158 #endif\r
8159 }\r
8160 \r
8161 VOID\r
8162 SetGNUMode()\r
8163 {\r
8164   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8165 }\r
8166 \r
8167 VOID\r
8168 SetNCPMode()\r
8169 {\r
8170   HMENU hmenu = GetMenu(hwndMain);\r
8171   SetMenuEnables(hmenu, ncpEnables);\r
8172     DrawMenuBar(hwndMain);\r
8173 }\r
8174 \r
8175 VOID\r
8176 SetCmailMode()\r
8177 {\r
8178   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8179 }\r
8180 \r
8181 VOID \r
8182 SetTrainingModeOn()\r
8183 {\r
8184   int i;\r
8185   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8186   for (i = 0; i < N_BUTTONS; i++) {\r
8187     if (buttonDesc[i].hwnd != NULL)\r
8188       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8189   }\r
8190   CommentPopDown();\r
8191 }\r
8192 \r
8193 VOID SetTrainingModeOff()\r
8194 {\r
8195   int i;\r
8196   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8197   for (i = 0; i < N_BUTTONS; i++) {\r
8198     if (buttonDesc[i].hwnd != NULL)\r
8199       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8200   }\r
8201 }\r
8202 \r
8203 \r
8204 VOID\r
8205 SetUserThinkingEnables()\r
8206 {\r
8207   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8208 }\r
8209 \r
8210 VOID\r
8211 SetMachineThinkingEnables()\r
8212 {\r
8213   HMENU hMenu = GetMenu(hwndMain);\r
8214   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8215 \r
8216   SetMenuEnables(hMenu, machineThinkingEnables);\r
8217 \r
8218   if (gameMode == MachinePlaysBlack) {\r
8219     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8220   } else if (gameMode == MachinePlaysWhite) {\r
8221     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8222   } else if (gameMode == TwoMachinesPlay) {\r
8223     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8224   }\r
8225 }\r
8226 \r
8227 \r
8228 VOID\r
8229 DisplayTitle(char *str)\r
8230 {\r
8231   char title[MSG_SIZ], *host;\r
8232   if (str[0] != NULLCHAR) {\r
8233     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8234   } else if (appData.icsActive) {\r
8235     if (appData.icsCommPort[0] != NULLCHAR)\r
8236       host = "ICS";\r
8237     else \r
8238       host = appData.icsHost;\r
8239       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8240   } else if (appData.noChessProgram) {\r
8241     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8242   } else {\r
8243     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8244     strcat(title, ": ");\r
8245     strcat(title, first.tidy);\r
8246   }\r
8247   SetWindowText(hwndMain, title);\r
8248 }\r
8249 \r
8250 \r
8251 VOID\r
8252 DisplayMessage(char *str1, char *str2)\r
8253 {\r
8254   HDC hdc;\r
8255   HFONT oldFont;\r
8256   int remain = MESSAGE_TEXT_MAX - 1;\r
8257   int len;\r
8258 \r
8259   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8260   messageText[0] = NULLCHAR;\r
8261   if (*str1) {\r
8262     len = strlen(str1);\r
8263     if (len > remain) len = remain;\r
8264     strncpy(messageText, str1, len);\r
8265     messageText[len] = NULLCHAR;\r
8266     remain -= len;\r
8267   }\r
8268   if (*str2 && remain >= 2) {\r
8269     if (*str1) {\r
8270       strcat(messageText, "  ");\r
8271       remain -= 2;\r
8272     }\r
8273     len = strlen(str2);\r
8274     if (len > remain) len = remain;\r
8275     strncat(messageText, str2, len);\r
8276   }\r
8277   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8278   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8279 \r
8280   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8281 \r
8282   SAYMACHINEMOVE();\r
8283 \r
8284   hdc = GetDC(hwndMain);\r
8285   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8286   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8287              &messageRect, messageText, strlen(messageText), NULL);\r
8288   (void) SelectObject(hdc, oldFont);\r
8289   (void) ReleaseDC(hwndMain, hdc);\r
8290 }\r
8291 \r
8292 VOID\r
8293 DisplayError(char *str, int error)\r
8294 {\r
8295   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8296   int len;\r
8297 \r
8298   if (error == 0) {\r
8299     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8300   } else {\r
8301     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8302                         NULL, error, LANG_NEUTRAL,\r
8303                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8304     if (len > 0) {\r
8305       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8306     } else {\r
8307       ErrorMap *em = errmap;\r
8308       while (em->err != 0 && em->err != error) em++;\r
8309       if (em->err != 0) {\r
8310         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8311       } else {\r
8312         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8313       }\r
8314     }\r
8315   }\r
8316   \r
8317   ErrorPopUp(_("Error"), buf);\r
8318 }\r
8319 \r
8320 \r
8321 VOID\r
8322 DisplayMoveError(char *str)\r
8323 {\r
8324   fromX = fromY = -1;\r
8325   ClearHighlights();\r
8326   DrawPosition(FALSE, NULL);\r
8327   if (appData.popupMoveErrors) {\r
8328     ErrorPopUp(_("Error"), str);\r
8329   } else {\r
8330     DisplayMessage(str, "");\r
8331     moveErrorMessageUp = TRUE;\r
8332   }\r
8333 }\r
8334 \r
8335 VOID\r
8336 DisplayFatalError(char *str, int error, int exitStatus)\r
8337 {\r
8338   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8339   int len;\r
8340   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8341 \r
8342   if (error != 0) {\r
8343     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8344                         NULL, error, LANG_NEUTRAL,\r
8345                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8346     if (len > 0) {\r
8347       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8348     } else {\r
8349       ErrorMap *em = errmap;\r
8350       while (em->err != 0 && em->err != error) em++;\r
8351       if (em->err != 0) {\r
8352         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8353       } else {\r
8354         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8355       }\r
8356     }\r
8357     str = buf;\r
8358   }\r
8359   if (appData.debugMode) {\r
8360     fprintf(debugFP, "%s: %s\n", label, str);\r
8361   }\r
8362   if (appData.popupExitMessage) {\r
8363     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8364                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8365   }\r
8366   ExitEvent(exitStatus);\r
8367 }\r
8368 \r
8369 \r
8370 VOID\r
8371 DisplayInformation(char *str)\r
8372 {\r
8373   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8374 }\r
8375 \r
8376 \r
8377 VOID\r
8378 DisplayNote(char *str)\r
8379 {\r
8380   ErrorPopUp(_("Note"), str);\r
8381 }\r
8382 \r
8383 \r
8384 typedef struct {\r
8385   char *title, *question, *replyPrefix;\r
8386   ProcRef pr;\r
8387 } QuestionParams;\r
8388 \r
8389 LRESULT CALLBACK\r
8390 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8391 {\r
8392   static QuestionParams *qp;\r
8393   char reply[MSG_SIZ];\r
8394   int len, err;\r
8395 \r
8396   switch (message) {\r
8397   case WM_INITDIALOG:\r
8398     qp = (QuestionParams *) lParam;\r
8399     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8400     Translate(hDlg, DLG_Question);\r
8401     SetWindowText(hDlg, qp->title);\r
8402     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8403     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8404     return FALSE;\r
8405 \r
8406   case WM_COMMAND:\r
8407     switch (LOWORD(wParam)) {\r
8408     case IDOK:\r
8409       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8410       if (*reply) strcat(reply, " ");\r
8411       len = strlen(reply);\r
8412       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8413       strcat(reply, "\n");\r
8414       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8415       EndDialog(hDlg, TRUE);\r
8416       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8417       return TRUE;\r
8418     case IDCANCEL:\r
8419       EndDialog(hDlg, FALSE);\r
8420       return TRUE;\r
8421     default:\r
8422       break;\r
8423     }\r
8424     break;\r
8425   }\r
8426   return FALSE;\r
8427 }\r
8428 \r
8429 VOID\r
8430 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8431 {\r
8432     QuestionParams qp;\r
8433     FARPROC lpProc;\r
8434     \r
8435     qp.title = title;\r
8436     qp.question = question;\r
8437     qp.replyPrefix = replyPrefix;\r
8438     qp.pr = pr;\r
8439     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8440     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8441       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8442     FreeProcInstance(lpProc);\r
8443 }\r
8444 \r
8445 /* [AS] Pick FRC position */\r
8446 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8447 {\r
8448     static int * lpIndexFRC;\r
8449     BOOL index_is_ok;\r
8450     char buf[16];\r
8451 \r
8452     switch( message )\r
8453     {\r
8454     case WM_INITDIALOG:\r
8455         lpIndexFRC = (int *) lParam;\r
8456 \r
8457         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8458         Translate(hDlg, DLG_NewGameFRC);\r
8459 \r
8460         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8461         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8462         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8463         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8464 \r
8465         break;\r
8466 \r
8467     case WM_COMMAND:\r
8468         switch( LOWORD(wParam) ) {\r
8469         case IDOK:\r
8470             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8471             EndDialog( hDlg, 0 );\r
8472             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8473             return TRUE;\r
8474         case IDCANCEL:\r
8475             EndDialog( hDlg, 1 );   \r
8476             return TRUE;\r
8477         case IDC_NFG_Edit:\r
8478             if( HIWORD(wParam) == EN_CHANGE ) {\r
8479                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8480 \r
8481                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8482             }\r
8483             return TRUE;\r
8484         case IDC_NFG_Random:\r
8485           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8486             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\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 NewGameFRC()\r
8497 {\r
8498     int result;\r
8499     int index = appData.defaultFrcPosition;\r
8500     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8501 \r
8502     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8503 \r
8504     if( result == 0 ) {\r
8505         appData.defaultFrcPosition = index;\r
8506     }\r
8507 \r
8508     return result;\r
8509 }\r
8510 \r
8511 /* [AS] Game list options. Refactored by HGM */\r
8512 \r
8513 HWND gameListOptionsDialog;\r
8514 \r
8515 // low-level front-end: clear text edit / list widget\r
8516 void\r
8517 GLT_ClearList()\r
8518 {\r
8519     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8520 }\r
8521 \r
8522 // low-level front-end: clear text edit / list widget\r
8523 void\r
8524 GLT_DeSelectList()\r
8525 {\r
8526     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8527 }\r
8528 \r
8529 // low-level front-end: append line to text edit / list widget\r
8530 void\r
8531 GLT_AddToList( char *name )\r
8532 {\r
8533     if( name != 0 ) {\r
8534             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8535     }\r
8536 }\r
8537 \r
8538 // low-level front-end: get line from text edit / list widget\r
8539 Boolean\r
8540 GLT_GetFromList( int index, char *name )\r
8541 {\r
8542     if( name != 0 ) {\r
8543             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8544                 return TRUE;\r
8545     }\r
8546     return FALSE;\r
8547 }\r
8548 \r
8549 void GLT_MoveSelection( HWND hDlg, int delta )\r
8550 {\r
8551     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8552     int idx2 = idx1 + delta;\r
8553     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8554 \r
8555     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8556         char buf[128];\r
8557 \r
8558         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8559         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8560         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8561         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8562     }\r
8563 }\r
8564 \r
8565 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8566 {\r
8567     switch( message )\r
8568     {\r
8569     case WM_INITDIALOG:\r
8570         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8571         \r
8572         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8573         Translate(hDlg, DLG_GameListOptions);\r
8574 \r
8575         /* Initialize list */\r
8576         GLT_TagsToList( lpUserGLT );\r
8577 \r
8578         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8579 \r
8580         break;\r
8581 \r
8582     case WM_COMMAND:\r
8583         switch( LOWORD(wParam) ) {\r
8584         case IDOK:\r
8585             GLT_ParseList();\r
8586             EndDialog( hDlg, 0 );\r
8587             return TRUE;\r
8588         case IDCANCEL:\r
8589             EndDialog( hDlg, 1 );\r
8590             return TRUE;\r
8591 \r
8592         case IDC_GLT_Default:\r
8593             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8594             return TRUE;\r
8595 \r
8596         case IDC_GLT_Restore:\r
8597             GLT_TagsToList( appData.gameListTags );\r
8598             return TRUE;\r
8599 \r
8600         case IDC_GLT_Up:\r
8601             GLT_MoveSelection( hDlg, -1 );\r
8602             return TRUE;\r
8603 \r
8604         case IDC_GLT_Down:\r
8605             GLT_MoveSelection( hDlg, +1 );\r
8606             return TRUE;\r
8607         }\r
8608 \r
8609         break;\r
8610     }\r
8611 \r
8612     return FALSE;\r
8613 }\r
8614 \r
8615 int GameListOptions()\r
8616 {\r
8617     int result;\r
8618     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8619 \r
8620       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8621 \r
8622     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8623 \r
8624     if( result == 0 ) {\r
8625         /* [AS] Memory leak here! */\r
8626         appData.gameListTags = strdup( lpUserGLT ); \r
8627     }\r
8628 \r
8629     return result;\r
8630 }\r
8631 \r
8632 VOID\r
8633 DisplayIcsInteractionTitle(char *str)\r
8634 {\r
8635   char consoleTitle[MSG_SIZ];\r
8636 \r
8637     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8638     SetWindowText(hwndConsole, consoleTitle);\r
8639 \r
8640     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8641       char buf[MSG_SIZ], *p = buf, *q;\r
8642         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8643       do {\r
8644         q = strchr(p, ';');\r
8645         if(q) *q++ = 0;\r
8646         if(*p) ChatPopUp(p);\r
8647       } while(p=q);\r
8648     }\r
8649 \r
8650     SetActiveWindow(hwndMain);\r
8651 }\r
8652 \r
8653 void\r
8654 DrawPosition(int fullRedraw, Board board)\r
8655 {\r
8656   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8657 }\r
8658 \r
8659 void NotifyFrontendLogin()\r
8660 {\r
8661         if (hwndConsole)\r
8662                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8663 }\r
8664 \r
8665 VOID\r
8666 ResetFrontEnd()\r
8667 {\r
8668   fromX = fromY = -1;\r
8669   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8670     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8671     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8672     dragInfo.lastpos = dragInfo.pos;\r
8673     dragInfo.start.x = dragInfo.start.y = -1;\r
8674     dragInfo.from = dragInfo.start;\r
8675     ReleaseCapture();\r
8676     DrawPosition(TRUE, NULL);\r
8677   }\r
8678   TagsPopDown();\r
8679 }\r
8680 \r
8681 \r
8682 VOID\r
8683 CommentPopUp(char *title, char *str)\r
8684 {\r
8685   HWND hwnd = GetActiveWindow();\r
8686   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8687   SAY(str);\r
8688   SetActiveWindow(hwnd);\r
8689 }\r
8690 \r
8691 VOID\r
8692 CommentPopDown(void)\r
8693 {\r
8694   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8695   if (commentDialog) {\r
8696     ShowWindow(commentDialog, SW_HIDE);\r
8697   }\r
8698   commentUp = FALSE;\r
8699 }\r
8700 \r
8701 VOID\r
8702 EditCommentPopUp(int index, char *title, char *str)\r
8703 {\r
8704   EitherCommentPopUp(index, title, str, TRUE);\r
8705 }\r
8706 \r
8707 \r
8708 VOID\r
8709 RingBell()\r
8710 {\r
8711   MyPlaySound(&sounds[(int)SoundMove]);\r
8712 }\r
8713 \r
8714 VOID PlayIcsWinSound()\r
8715 {\r
8716   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8717 }\r
8718 \r
8719 VOID PlayIcsLossSound()\r
8720 {\r
8721   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8722 }\r
8723 \r
8724 VOID PlayIcsDrawSound()\r
8725 {\r
8726   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8727 }\r
8728 \r
8729 VOID PlayIcsUnfinishedSound()\r
8730 {\r
8731   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8732 }\r
8733 \r
8734 VOID\r
8735 PlayAlarmSound()\r
8736 {\r
8737   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8738 }\r
8739 \r
8740 VOID\r
8741 PlayTellSound()\r
8742 {\r
8743   MyPlaySound(&textAttribs[ColorTell].sound);\r
8744 }\r
8745 \r
8746 \r
8747 VOID\r
8748 EchoOn()\r
8749 {\r
8750   HWND hInput;\r
8751   consoleEcho = TRUE;\r
8752   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8753   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8754   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8755 }\r
8756 \r
8757 \r
8758 VOID\r
8759 EchoOff()\r
8760 {\r
8761   CHARFORMAT cf;\r
8762   HWND hInput;\r
8763   consoleEcho = FALSE;\r
8764   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8765   /* This works OK: set text and background both to the same color */\r
8766   cf = consoleCF;\r
8767   cf.crTextColor = COLOR_ECHOOFF;\r
8768   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8769   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8770 }\r
8771 \r
8772 /* No Raw()...? */\r
8773 \r
8774 void Colorize(ColorClass cc, int continuation)\r
8775 {\r
8776   currentColorClass = cc;\r
8777   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8778   consoleCF.crTextColor = textAttribs[cc].color;\r
8779   consoleCF.dwEffects = textAttribs[cc].effects;\r
8780   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8781 }\r
8782 \r
8783 char *\r
8784 UserName()\r
8785 {\r
8786   static char buf[MSG_SIZ];\r
8787   DWORD bufsiz = MSG_SIZ;\r
8788 \r
8789   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8790         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8791   }\r
8792   if (!GetUserName(buf, &bufsiz)) {\r
8793     /*DisplayError("Error getting user name", GetLastError());*/\r
8794     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8795   }\r
8796   return buf;\r
8797 }\r
8798 \r
8799 char *\r
8800 HostName()\r
8801 {\r
8802   static char buf[MSG_SIZ];\r
8803   DWORD bufsiz = MSG_SIZ;\r
8804 \r
8805   if (!GetComputerName(buf, &bufsiz)) {\r
8806     /*DisplayError("Error getting host name", GetLastError());*/\r
8807     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8808   }\r
8809   return buf;\r
8810 }\r
8811 \r
8812 \r
8813 int\r
8814 ClockTimerRunning()\r
8815 {\r
8816   return clockTimerEvent != 0;\r
8817 }\r
8818 \r
8819 int\r
8820 StopClockTimer()\r
8821 {\r
8822   if (clockTimerEvent == 0) return FALSE;\r
8823   KillTimer(hwndMain, clockTimerEvent);\r
8824   clockTimerEvent = 0;\r
8825   return TRUE;\r
8826 }\r
8827 \r
8828 void\r
8829 StartClockTimer(long millisec)\r
8830 {\r
8831   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8832                              (UINT) millisec, NULL);\r
8833 }\r
8834 \r
8835 void\r
8836 DisplayWhiteClock(long timeRemaining, int highlight)\r
8837 {\r
8838   HDC hdc;\r
8839   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8840 \r
8841   if(appData.noGUI) return;\r
8842   hdc = GetDC(hwndMain);\r
8843   if (!IsIconic(hwndMain)) {\r
8844     DisplayAClock(hdc, timeRemaining, highlight, \r
8845                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8846   }\r
8847   if (highlight && iconCurrent == iconBlack) {\r
8848     iconCurrent = iconWhite;\r
8849     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8850     if (IsIconic(hwndMain)) {\r
8851       DrawIcon(hdc, 2, 2, iconCurrent);\r
8852     }\r
8853   }\r
8854   (void) ReleaseDC(hwndMain, hdc);\r
8855   if (hwndConsole)\r
8856     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8857 }\r
8858 \r
8859 void\r
8860 DisplayBlackClock(long timeRemaining, int highlight)\r
8861 {\r
8862   HDC hdc;\r
8863   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8864 \r
8865   if(appData.noGUI) return;\r
8866   hdc = GetDC(hwndMain);\r
8867   if (!IsIconic(hwndMain)) {\r
8868     DisplayAClock(hdc, timeRemaining, highlight, \r
8869                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8870   }\r
8871   if (highlight && iconCurrent == iconWhite) {\r
8872     iconCurrent = iconBlack;\r
8873     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8874     if (IsIconic(hwndMain)) {\r
8875       DrawIcon(hdc, 2, 2, iconCurrent);\r
8876     }\r
8877   }\r
8878   (void) ReleaseDC(hwndMain, hdc);\r
8879   if (hwndConsole)\r
8880     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8881 }\r
8882 \r
8883 \r
8884 int\r
8885 LoadGameTimerRunning()\r
8886 {\r
8887   return loadGameTimerEvent != 0;\r
8888 }\r
8889 \r
8890 int\r
8891 StopLoadGameTimer()\r
8892 {\r
8893   if (loadGameTimerEvent == 0) return FALSE;\r
8894   KillTimer(hwndMain, loadGameTimerEvent);\r
8895   loadGameTimerEvent = 0;\r
8896   return TRUE;\r
8897 }\r
8898 \r
8899 void\r
8900 StartLoadGameTimer(long millisec)\r
8901 {\r
8902   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8903                                 (UINT) millisec, NULL);\r
8904 }\r
8905 \r
8906 void\r
8907 AutoSaveGame()\r
8908 {\r
8909   char *defName;\r
8910   FILE *f;\r
8911   char fileTitle[MSG_SIZ];\r
8912 \r
8913   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8914   f = OpenFileDialog(hwndMain, "a", defName,\r
8915                      appData.oldSaveStyle ? "gam" : "pgn",\r
8916                      GAME_FILT, \r
8917                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8918   if (f != NULL) {\r
8919     SaveGame(f, 0, "");\r
8920     fclose(f);\r
8921   }\r
8922 }\r
8923 \r
8924 \r
8925 void\r
8926 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8927 {\r
8928   if (delayedTimerEvent != 0) {\r
8929     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8930       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8931     }\r
8932     KillTimer(hwndMain, delayedTimerEvent);\r
8933     delayedTimerEvent = 0;\r
8934     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8935     delayedTimerCallback();\r
8936   }\r
8937   delayedTimerCallback = cb;\r
8938   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8939                                 (UINT) millisec, NULL);\r
8940 }\r
8941 \r
8942 DelayedEventCallback\r
8943 GetDelayedEvent()\r
8944 {\r
8945   if (delayedTimerEvent) {\r
8946     return delayedTimerCallback;\r
8947   } else {\r
8948     return NULL;\r
8949   }\r
8950 }\r
8951 \r
8952 void\r
8953 CancelDelayedEvent()\r
8954 {\r
8955   if (delayedTimerEvent) {\r
8956     KillTimer(hwndMain, delayedTimerEvent);\r
8957     delayedTimerEvent = 0;\r
8958   }\r
8959 }\r
8960 \r
8961 DWORD GetWin32Priority(int nice)\r
8962 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8963 /*\r
8964 REALTIME_PRIORITY_CLASS     0x00000100\r
8965 HIGH_PRIORITY_CLASS         0x00000080\r
8966 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8967 NORMAL_PRIORITY_CLASS       0x00000020\r
8968 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8969 IDLE_PRIORITY_CLASS         0x00000040\r
8970 */\r
8971         if (nice < -15) return 0x00000080;\r
8972         if (nice < 0)   return 0x00008000;\r
8973         if (nice == 0)  return 0x00000020;\r
8974         if (nice < 15)  return 0x00004000;\r
8975         return 0x00000040;\r
8976 }\r
8977 \r
8978 void RunCommand(char *cmdLine)\r
8979 {\r
8980   /* Now create the child process. */\r
8981   STARTUPINFO siStartInfo;\r
8982   PROCESS_INFORMATION piProcInfo;\r
8983 \r
8984   siStartInfo.cb = sizeof(STARTUPINFO);\r
8985   siStartInfo.lpReserved = NULL;\r
8986   siStartInfo.lpDesktop = NULL;\r
8987   siStartInfo.lpTitle = NULL;\r
8988   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8989   siStartInfo.cbReserved2 = 0;\r
8990   siStartInfo.lpReserved2 = NULL;\r
8991   siStartInfo.hStdInput = NULL;\r
8992   siStartInfo.hStdOutput = NULL;\r
8993   siStartInfo.hStdError = NULL;\r
8994 \r
8995   CreateProcess(NULL,\r
8996                 cmdLine,           /* command line */\r
8997                 NULL,      /* process security attributes */\r
8998                 NULL,      /* primary thread security attrs */\r
8999                 TRUE,      /* handles are inherited */\r
9000                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9001                 NULL,      /* use parent's environment */\r
9002                 NULL,\r
9003                 &siStartInfo, /* STARTUPINFO pointer */\r
9004                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9005 \r
9006   CloseHandle(piProcInfo.hThread);\r
9007 }\r
9008 \r
9009 /* Start a child process running the given program.\r
9010    The process's standard output can be read from "from", and its\r
9011    standard input can be written to "to".\r
9012    Exit with fatal error if anything goes wrong.\r
9013    Returns an opaque pointer that can be used to destroy the process\r
9014    later.\r
9015 */\r
9016 int\r
9017 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9018 {\r
9019 #define BUFSIZE 4096\r
9020 \r
9021   HANDLE hChildStdinRd, hChildStdinWr,\r
9022     hChildStdoutRd, hChildStdoutWr;\r
9023   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9024   SECURITY_ATTRIBUTES saAttr;\r
9025   BOOL fSuccess;\r
9026   PROCESS_INFORMATION piProcInfo;\r
9027   STARTUPINFO siStartInfo;\r
9028   ChildProc *cp;\r
9029   char buf[MSG_SIZ];\r
9030   DWORD err;\r
9031 \r
9032   if (appData.debugMode) {\r
9033     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9034   }\r
9035 \r
9036   *pr = NoProc;\r
9037 \r
9038   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9039   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9040   saAttr.bInheritHandle = TRUE;\r
9041   saAttr.lpSecurityDescriptor = NULL;\r
9042 \r
9043   /*\r
9044    * The steps for redirecting child's STDOUT:\r
9045    *     1. Create anonymous pipe to be STDOUT for child.\r
9046    *     2. Create a noninheritable duplicate of read handle,\r
9047    *         and close the inheritable read handle.\r
9048    */\r
9049 \r
9050   /* Create a pipe for the child's STDOUT. */\r
9051   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9052     return GetLastError();\r
9053   }\r
9054 \r
9055   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9056   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9057                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9058                              FALSE,     /* not inherited */\r
9059                              DUPLICATE_SAME_ACCESS);\r
9060   if (! fSuccess) {\r
9061     return GetLastError();\r
9062   }\r
9063   CloseHandle(hChildStdoutRd);\r
9064 \r
9065   /*\r
9066    * The steps for redirecting child's STDIN:\r
9067    *     1. Create anonymous pipe to be STDIN for child.\r
9068    *     2. Create a noninheritable duplicate of write handle,\r
9069    *         and close the inheritable write handle.\r
9070    */\r
9071 \r
9072   /* Create a pipe for the child's STDIN. */\r
9073   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9074     return GetLastError();\r
9075   }\r
9076 \r
9077   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9078   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9079                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9080                              FALSE,     /* not inherited */\r
9081                              DUPLICATE_SAME_ACCESS);\r
9082   if (! fSuccess) {\r
9083     return GetLastError();\r
9084   }\r
9085   CloseHandle(hChildStdinWr);\r
9086 \r
9087   /* Arrange to (1) look in dir for the child .exe file, and\r
9088    * (2) have dir be the child's working directory.  Interpret\r
9089    * dir relative to the directory WinBoard loaded from. */\r
9090   GetCurrentDirectory(MSG_SIZ, buf);\r
9091   SetCurrentDirectory(installDir);\r
9092   SetCurrentDirectory(dir);\r
9093 \r
9094   /* Now create the child process. */\r
9095 \r
9096   siStartInfo.cb = sizeof(STARTUPINFO);\r
9097   siStartInfo.lpReserved = NULL;\r
9098   siStartInfo.lpDesktop = NULL;\r
9099   siStartInfo.lpTitle = NULL;\r
9100   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9101   siStartInfo.cbReserved2 = 0;\r
9102   siStartInfo.lpReserved2 = NULL;\r
9103   siStartInfo.hStdInput = hChildStdinRd;\r
9104   siStartInfo.hStdOutput = hChildStdoutWr;\r
9105   siStartInfo.hStdError = hChildStdoutWr;\r
9106 \r
9107   fSuccess = CreateProcess(NULL,\r
9108                            cmdLine,        /* command line */\r
9109                            NULL,           /* process security attributes */\r
9110                            NULL,           /* primary thread security attrs */\r
9111                            TRUE,           /* handles are inherited */\r
9112                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9113                            NULL,           /* use parent's environment */\r
9114                            NULL,\r
9115                            &siStartInfo, /* STARTUPINFO pointer */\r
9116                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9117 \r
9118   err = GetLastError();\r
9119   SetCurrentDirectory(buf); /* return to prev directory */\r
9120   if (! fSuccess) {\r
9121     return err;\r
9122   }\r
9123 \r
9124   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9125     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9126     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9127   }\r
9128 \r
9129   /* Close the handles we don't need in the parent */\r
9130   CloseHandle(piProcInfo.hThread);\r
9131   CloseHandle(hChildStdinRd);\r
9132   CloseHandle(hChildStdoutWr);\r
9133 \r
9134   /* Prepare return value */\r
9135   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9136   cp->kind = CPReal;\r
9137   cp->hProcess = piProcInfo.hProcess;\r
9138   cp->pid = piProcInfo.dwProcessId;\r
9139   cp->hFrom = hChildStdoutRdDup;\r
9140   cp->hTo = hChildStdinWrDup;\r
9141 \r
9142   *pr = (void *) cp;\r
9143 \r
9144   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9145      2000 where engines sometimes don't see the initial command(s)\r
9146      from WinBoard and hang.  I don't understand how that can happen,\r
9147      but the Sleep is harmless, so I've put it in.  Others have also\r
9148      reported what may be the same problem, so hopefully this will fix\r
9149      it for them too.  */\r
9150   Sleep(500);\r
9151 \r
9152   return NO_ERROR;\r
9153 }\r
9154 \r
9155 \r
9156 void\r
9157 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9158 {\r
9159   ChildProc *cp; int result;\r
9160 \r
9161   cp = (ChildProc *) pr;\r
9162   if (cp == NULL) return;\r
9163 \r
9164   switch (cp->kind) {\r
9165   case CPReal:\r
9166     /* TerminateProcess is considered harmful, so... */\r
9167     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9168     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9169     /* The following doesn't work because the chess program\r
9170        doesn't "have the same console" as WinBoard.  Maybe\r
9171        we could arrange for this even though neither WinBoard\r
9172        nor the chess program uses a console for stdio? */\r
9173     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9174 \r
9175     /* [AS] Special termination modes for misbehaving programs... */\r
9176     if( signal == 9 ) { \r
9177         result = TerminateProcess( cp->hProcess, 0 );\r
9178 \r
9179         if ( appData.debugMode) {\r
9180             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9181         }\r
9182     }\r
9183     else if( signal == 10 ) {\r
9184         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9185 \r
9186         if( dw != WAIT_OBJECT_0 ) {\r
9187             result = TerminateProcess( cp->hProcess, 0 );\r
9188 \r
9189             if ( appData.debugMode) {\r
9190                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9191             }\r
9192 \r
9193         }\r
9194     }\r
9195 \r
9196     CloseHandle(cp->hProcess);\r
9197     break;\r
9198 \r
9199   case CPComm:\r
9200     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9201     break;\r
9202 \r
9203   case CPSock:\r
9204     closesocket(cp->sock);\r
9205     WSACleanup();\r
9206     break;\r
9207 \r
9208   case CPRcmd:\r
9209     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9210     closesocket(cp->sock);\r
9211     closesocket(cp->sock2);\r
9212     WSACleanup();\r
9213     break;\r
9214   }\r
9215   free(cp);\r
9216 }\r
9217 \r
9218 void\r
9219 InterruptChildProcess(ProcRef pr)\r
9220 {\r
9221   ChildProc *cp;\r
9222 \r
9223   cp = (ChildProc *) pr;\r
9224   if (cp == NULL) return;\r
9225   switch (cp->kind) {\r
9226   case CPReal:\r
9227     /* The following doesn't work because the chess program\r
9228        doesn't "have the same console" as WinBoard.  Maybe\r
9229        we could arrange for this even though neither WinBoard\r
9230        nor the chess program uses a console for stdio */\r
9231     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9232     break;\r
9233 \r
9234   case CPComm:\r
9235   case CPSock:\r
9236     /* Can't interrupt */\r
9237     break;\r
9238 \r
9239   case CPRcmd:\r
9240     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9241     break;\r
9242   }\r
9243 }\r
9244 \r
9245 \r
9246 int\r
9247 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9248 {\r
9249   char cmdLine[MSG_SIZ];\r
9250 \r
9251   if (port[0] == NULLCHAR) {\r
9252     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9253   } else {\r
9254     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9255   }\r
9256   return StartChildProcess(cmdLine, "", pr);\r
9257 }\r
9258 \r
9259 \r
9260 /* Code to open TCP sockets */\r
9261 \r
9262 int\r
9263 OpenTCP(char *host, char *port, ProcRef *pr)\r
9264 {\r
9265   ChildProc *cp;\r
9266   int err;\r
9267   SOCKET s;\r
9268 \r
9269   struct sockaddr_in sa, mysa;\r
9270   struct hostent FAR *hp;\r
9271   unsigned short uport;\r
9272   WORD wVersionRequested;\r
9273   WSADATA wsaData;\r
9274 \r
9275   /* Initialize socket DLL */\r
9276   wVersionRequested = MAKEWORD(1, 1);\r
9277   err = WSAStartup(wVersionRequested, &wsaData);\r
9278   if (err != 0) return err;\r
9279 \r
9280   /* Make socket */\r
9281   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9282     err = WSAGetLastError();\r
9283     WSACleanup();\r
9284     return err;\r
9285   }\r
9286 \r
9287   /* Bind local address using (mostly) don't-care values.\r
9288    */\r
9289   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9290   mysa.sin_family = AF_INET;\r
9291   mysa.sin_addr.s_addr = INADDR_ANY;\r
9292   uport = (unsigned short) 0;\r
9293   mysa.sin_port = htons(uport);\r
9294   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9295       == SOCKET_ERROR) {\r
9296     err = WSAGetLastError();\r
9297     WSACleanup();\r
9298     return err;\r
9299   }\r
9300 \r
9301   /* Resolve remote host name */\r
9302   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9303   if (!(hp = gethostbyname(host))) {\r
9304     unsigned int b0, b1, b2, b3;\r
9305 \r
9306     err = WSAGetLastError();\r
9307 \r
9308     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9309       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9310       hp->h_addrtype = AF_INET;\r
9311       hp->h_length = 4;\r
9312       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9313       hp->h_addr_list[0] = (char *) malloc(4);\r
9314       hp->h_addr_list[0][0] = (char) b0;\r
9315       hp->h_addr_list[0][1] = (char) b1;\r
9316       hp->h_addr_list[0][2] = (char) b2;\r
9317       hp->h_addr_list[0][3] = (char) b3;\r
9318     } else {\r
9319       WSACleanup();\r
9320       return err;\r
9321     }\r
9322   }\r
9323   sa.sin_family = hp->h_addrtype;\r
9324   uport = (unsigned short) atoi(port);\r
9325   sa.sin_port = htons(uport);\r
9326   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9327 \r
9328   /* Make connection */\r
9329   if (connect(s, (struct sockaddr *) &sa,\r
9330               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9331     err = WSAGetLastError();\r
9332     WSACleanup();\r
9333     return err;\r
9334   }\r
9335 \r
9336   /* Prepare return value */\r
9337   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9338   cp->kind = CPSock;\r
9339   cp->sock = s;\r
9340   *pr = (ProcRef *) cp;\r
9341 \r
9342   return NO_ERROR;\r
9343 }\r
9344 \r
9345 int\r
9346 OpenCommPort(char *name, ProcRef *pr)\r
9347 {\r
9348   HANDLE h;\r
9349   COMMTIMEOUTS ct;\r
9350   ChildProc *cp;\r
9351   char fullname[MSG_SIZ];\r
9352 \r
9353   if (*name != '\\')\r
9354     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9355   else\r
9356     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9357 \r
9358   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9359                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9360   if (h == (HANDLE) -1) {\r
9361     return GetLastError();\r
9362   }\r
9363   hCommPort = h;\r
9364 \r
9365   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9366 \r
9367   /* Accumulate characters until a 100ms pause, then parse */\r
9368   ct.ReadIntervalTimeout = 100;\r
9369   ct.ReadTotalTimeoutMultiplier = 0;\r
9370   ct.ReadTotalTimeoutConstant = 0;\r
9371   ct.WriteTotalTimeoutMultiplier = 0;\r
9372   ct.WriteTotalTimeoutConstant = 0;\r
9373   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9374 \r
9375   /* Prepare return value */\r
9376   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9377   cp->kind = CPComm;\r
9378   cp->hFrom = h;\r
9379   cp->hTo = h;\r
9380   *pr = (ProcRef *) cp;\r
9381 \r
9382   return NO_ERROR;\r
9383 }\r
9384 \r
9385 int\r
9386 OpenLoopback(ProcRef *pr)\r
9387 {\r
9388   DisplayFatalError(_("Not implemented"), 0, 1);\r
9389   return NO_ERROR;\r
9390 }\r
9391 \r
9392 \r
9393 int\r
9394 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9395 {\r
9396   ChildProc *cp;\r
9397   int err;\r
9398   SOCKET s, s2, s3;\r
9399   struct sockaddr_in sa, mysa;\r
9400   struct hostent FAR *hp;\r
9401   unsigned short uport;\r
9402   WORD wVersionRequested;\r
9403   WSADATA wsaData;\r
9404   int fromPort;\r
9405   char stderrPortStr[MSG_SIZ];\r
9406 \r
9407   /* Initialize socket DLL */\r
9408   wVersionRequested = MAKEWORD(1, 1);\r
9409   err = WSAStartup(wVersionRequested, &wsaData);\r
9410   if (err != 0) return err;\r
9411 \r
9412   /* Resolve remote host name */\r
9413   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9414   if (!(hp = gethostbyname(host))) {\r
9415     unsigned int b0, b1, b2, b3;\r
9416 \r
9417     err = WSAGetLastError();\r
9418 \r
9419     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9420       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9421       hp->h_addrtype = AF_INET;\r
9422       hp->h_length = 4;\r
9423       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9424       hp->h_addr_list[0] = (char *) malloc(4);\r
9425       hp->h_addr_list[0][0] = (char) b0;\r
9426       hp->h_addr_list[0][1] = (char) b1;\r
9427       hp->h_addr_list[0][2] = (char) b2;\r
9428       hp->h_addr_list[0][3] = (char) b3;\r
9429     } else {\r
9430       WSACleanup();\r
9431       return err;\r
9432     }\r
9433   }\r
9434   sa.sin_family = hp->h_addrtype;\r
9435   uport = (unsigned short) 514;\r
9436   sa.sin_port = htons(uport);\r
9437   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9438 \r
9439   /* Bind local socket to unused "privileged" port address\r
9440    */\r
9441   s = INVALID_SOCKET;\r
9442   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9443   mysa.sin_family = AF_INET;\r
9444   mysa.sin_addr.s_addr = INADDR_ANY;\r
9445   for (fromPort = 1023;; fromPort--) {\r
9446     if (fromPort < 0) {\r
9447       WSACleanup();\r
9448       return WSAEADDRINUSE;\r
9449     }\r
9450     if (s == INVALID_SOCKET) {\r
9451       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9452         err = WSAGetLastError();\r
9453         WSACleanup();\r
9454         return err;\r
9455       }\r
9456     }\r
9457     uport = (unsigned short) fromPort;\r
9458     mysa.sin_port = htons(uport);\r
9459     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9460         == SOCKET_ERROR) {\r
9461       err = WSAGetLastError();\r
9462       if (err == WSAEADDRINUSE) continue;\r
9463       WSACleanup();\r
9464       return err;\r
9465     }\r
9466     if (connect(s, (struct sockaddr *) &sa,\r
9467       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9468       err = WSAGetLastError();\r
9469       if (err == WSAEADDRINUSE) {\r
9470         closesocket(s);\r
9471         s = -1;\r
9472         continue;\r
9473       }\r
9474       WSACleanup();\r
9475       return err;\r
9476     }\r
9477     break;\r
9478   }\r
9479 \r
9480   /* Bind stderr local socket to unused "privileged" port address\r
9481    */\r
9482   s2 = INVALID_SOCKET;\r
9483   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9484   mysa.sin_family = AF_INET;\r
9485   mysa.sin_addr.s_addr = INADDR_ANY;\r
9486   for (fromPort = 1023;; fromPort--) {\r
9487     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9488     if (fromPort < 0) {\r
9489       (void) closesocket(s);\r
9490       WSACleanup();\r
9491       return WSAEADDRINUSE;\r
9492     }\r
9493     if (s2 == INVALID_SOCKET) {\r
9494       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9495         err = WSAGetLastError();\r
9496         closesocket(s);\r
9497         WSACleanup();\r
9498         return err;\r
9499       }\r
9500     }\r
9501     uport = (unsigned short) fromPort;\r
9502     mysa.sin_port = htons(uport);\r
9503     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9504         == SOCKET_ERROR) {\r
9505       err = WSAGetLastError();\r
9506       if (err == WSAEADDRINUSE) continue;\r
9507       (void) closesocket(s);\r
9508       WSACleanup();\r
9509       return err;\r
9510     }\r
9511     if (listen(s2, 1) == SOCKET_ERROR) {\r
9512       err = WSAGetLastError();\r
9513       if (err == WSAEADDRINUSE) {\r
9514         closesocket(s2);\r
9515         s2 = INVALID_SOCKET;\r
9516         continue;\r
9517       }\r
9518       (void) closesocket(s);\r
9519       (void) closesocket(s2);\r
9520       WSACleanup();\r
9521       return err;\r
9522     }\r
9523     break;\r
9524   }\r
9525   prevStderrPort = fromPort; // remember port used\r
9526   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9527 \r
9528   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9529     err = WSAGetLastError();\r
9530     (void) closesocket(s);\r
9531     (void) closesocket(s2);\r
9532     WSACleanup();\r
9533     return err;\r
9534   }\r
9535 \r
9536   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9537     err = WSAGetLastError();\r
9538     (void) closesocket(s);\r
9539     (void) closesocket(s2);\r
9540     WSACleanup();\r
9541     return err;\r
9542   }\r
9543   if (*user == NULLCHAR) user = UserName();\r
9544   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9545     err = WSAGetLastError();\r
9546     (void) closesocket(s);\r
9547     (void) closesocket(s2);\r
9548     WSACleanup();\r
9549     return err;\r
9550   }\r
9551   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9552     err = WSAGetLastError();\r
9553     (void) closesocket(s);\r
9554     (void) closesocket(s2);\r
9555     WSACleanup();\r
9556     return err;\r
9557   }\r
9558 \r
9559   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9560     err = WSAGetLastError();\r
9561     (void) closesocket(s);\r
9562     (void) closesocket(s2);\r
9563     WSACleanup();\r
9564     return err;\r
9565   }\r
9566   (void) closesocket(s2);  /* Stop listening */\r
9567 \r
9568   /* Prepare return value */\r
9569   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9570   cp->kind = CPRcmd;\r
9571   cp->sock = s;\r
9572   cp->sock2 = s3;\r
9573   *pr = (ProcRef *) cp;\r
9574 \r
9575   return NO_ERROR;\r
9576 }\r
9577 \r
9578 \r
9579 InputSourceRef\r
9580 AddInputSource(ProcRef pr, int lineByLine,\r
9581                InputCallback func, VOIDSTAR closure)\r
9582 {\r
9583   InputSource *is, *is2 = NULL;\r
9584   ChildProc *cp = (ChildProc *) pr;\r
9585 \r
9586   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9587   is->lineByLine = lineByLine;\r
9588   is->func = func;\r
9589   is->closure = closure;\r
9590   is->second = NULL;\r
9591   is->next = is->buf;\r
9592   if (pr == NoProc) {\r
9593     is->kind = CPReal;\r
9594     consoleInputSource = is;\r
9595   } else {\r
9596     is->kind = cp->kind;\r
9597     /* \r
9598         [AS] Try to avoid a race condition if the thread is given control too early:\r
9599         we create all threads suspended so that the is->hThread variable can be\r
9600         safely assigned, then let the threads start with ResumeThread.\r
9601     */\r
9602     switch (cp->kind) {\r
9603     case CPReal:\r
9604       is->hFile = cp->hFrom;\r
9605       cp->hFrom = NULL; /* now owned by InputThread */\r
9606       is->hThread =\r
9607         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9608                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9609       break;\r
9610 \r
9611     case CPComm:\r
9612       is->hFile = cp->hFrom;\r
9613       cp->hFrom = NULL; /* now owned by InputThread */\r
9614       is->hThread =\r
9615         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9616                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9617       break;\r
9618 \r
9619     case CPSock:\r
9620       is->sock = cp->sock;\r
9621       is->hThread =\r
9622         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9623                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9624       break;\r
9625 \r
9626     case CPRcmd:\r
9627       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9628       *is2 = *is;\r
9629       is->sock = cp->sock;\r
9630       is->second = is2;\r
9631       is2->sock = cp->sock2;\r
9632       is2->second = is2;\r
9633       is->hThread =\r
9634         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9635                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9636       is2->hThread =\r
9637         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9638                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9639       break;\r
9640     }\r
9641 \r
9642     if( is->hThread != NULL ) {\r
9643         ResumeThread( is->hThread );\r
9644     }\r
9645 \r
9646     if( is2 != NULL && is2->hThread != NULL ) {\r
9647         ResumeThread( is2->hThread );\r
9648     }\r
9649   }\r
9650 \r
9651   return (InputSourceRef) is;\r
9652 }\r
9653 \r
9654 void\r
9655 RemoveInputSource(InputSourceRef isr)\r
9656 {\r
9657   InputSource *is;\r
9658 \r
9659   is = (InputSource *) isr;\r
9660   is->hThread = NULL;  /* tell thread to stop */\r
9661   CloseHandle(is->hThread);\r
9662   if (is->second != NULL) {\r
9663     is->second->hThread = NULL;\r
9664     CloseHandle(is->second->hThread);\r
9665   }\r
9666 }\r
9667 \r
9668 int no_wrap(char *message, int count)\r
9669 {\r
9670     ConsoleOutput(message, count, FALSE);\r
9671     return count;\r
9672 }\r
9673 \r
9674 int\r
9675 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9676 {\r
9677   DWORD dOutCount;\r
9678   int outCount = SOCKET_ERROR;\r
9679   ChildProc *cp = (ChildProc *) pr;\r
9680   static OVERLAPPED ovl;\r
9681   static int line = 0;\r
9682 \r
9683   if (pr == NoProc)\r
9684   {\r
9685     if (appData.noJoin || !appData.useInternalWrap)\r
9686       return no_wrap(message, count);\r
9687     else\r
9688     {\r
9689       int width = get_term_width();\r
9690       int len = wrap(NULL, message, count, width, &line);\r
9691       char *msg = malloc(len);\r
9692       int dbgchk;\r
9693 \r
9694       if (!msg)\r
9695         return no_wrap(message, count);\r
9696       else\r
9697       {\r
9698         dbgchk = wrap(msg, message, count, width, &line);\r
9699         if (dbgchk != len && appData.debugMode)\r
9700             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9701         ConsoleOutput(msg, len, FALSE);\r
9702         free(msg);\r
9703         return len;\r
9704       }\r
9705     }\r
9706   }\r
9707 \r
9708   if (ovl.hEvent == NULL) {\r
9709     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9710   }\r
9711   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9712 \r
9713   switch (cp->kind) {\r
9714   case CPSock:\r
9715   case CPRcmd:\r
9716     outCount = send(cp->sock, message, count, 0);\r
9717     if (outCount == SOCKET_ERROR) {\r
9718       *outError = WSAGetLastError();\r
9719     } else {\r
9720       *outError = NO_ERROR;\r
9721     }\r
9722     break;\r
9723 \r
9724   case CPReal:\r
9725     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9726                   &dOutCount, NULL)) {\r
9727       *outError = NO_ERROR;\r
9728       outCount = (int) dOutCount;\r
9729     } else {\r
9730       *outError = GetLastError();\r
9731     }\r
9732     break;\r
9733 \r
9734   case CPComm:\r
9735     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9736                             &dOutCount, &ovl);\r
9737     if (*outError == NO_ERROR) {\r
9738       outCount = (int) dOutCount;\r
9739     }\r
9740     break;\r
9741   }\r
9742   return outCount;\r
9743 }\r
9744 \r
9745 void\r
9746 DoSleep(int n)\r
9747 {\r
9748     if(n != 0) Sleep(n);\r
9749 }\r
9750 \r
9751 int\r
9752 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9753                        long msdelay)\r
9754 {\r
9755   /* Ignore delay, not implemented for WinBoard */\r
9756   return OutputToProcess(pr, message, count, outError);\r
9757 }\r
9758 \r
9759 \r
9760 void\r
9761 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9762                         char *buf, int count, int error)\r
9763 {\r
9764   DisplayFatalError(_("Not implemented"), 0, 1);\r
9765 }\r
9766 \r
9767 /* see wgamelist.c for Game List functions */\r
9768 /* see wedittags.c for Edit Tags functions */\r
9769 \r
9770 \r
9771 int\r
9772 ICSInitScript()\r
9773 {\r
9774   FILE *f;\r
9775   char buf[MSG_SIZ];\r
9776   char *dummy;\r
9777 \r
9778   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9779     f = fopen(buf, "r");\r
9780     if (f != NULL) {\r
9781       ProcessICSInitScript(f);\r
9782       fclose(f);\r
9783       return TRUE;\r
9784     }\r
9785   }\r
9786   return FALSE;\r
9787 }\r
9788 \r
9789 \r
9790 VOID\r
9791 StartAnalysisClock()\r
9792 {\r
9793   if (analysisTimerEvent) return;\r
9794   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9795                                         (UINT) 2000, NULL);\r
9796 }\r
9797 \r
9798 VOID\r
9799 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9800 {\r
9801   highlightInfo.sq[0].x = fromX;\r
9802   highlightInfo.sq[0].y = fromY;\r
9803   highlightInfo.sq[1].x = toX;\r
9804   highlightInfo.sq[1].y = toY;\r
9805 }\r
9806 \r
9807 VOID\r
9808 ClearHighlights()\r
9809 {\r
9810   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9811     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9812 }\r
9813 \r
9814 VOID\r
9815 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9816 {\r
9817   premoveHighlightInfo.sq[0].x = fromX;\r
9818   premoveHighlightInfo.sq[0].y = fromY;\r
9819   premoveHighlightInfo.sq[1].x = toX;\r
9820   premoveHighlightInfo.sq[1].y = toY;\r
9821 }\r
9822 \r
9823 VOID\r
9824 ClearPremoveHighlights()\r
9825 {\r
9826   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9827     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9828 }\r
9829 \r
9830 VOID\r
9831 ShutDownFrontEnd()\r
9832 {\r
9833   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9834   DeleteClipboardTempFiles();\r
9835 }\r
9836 \r
9837 void\r
9838 BoardToTop()\r
9839 {\r
9840     if (IsIconic(hwndMain))\r
9841       ShowWindow(hwndMain, SW_RESTORE);\r
9842 \r
9843     SetActiveWindow(hwndMain);\r
9844 }\r
9845 \r
9846 /*\r
9847  * Prototypes for animation support routines\r
9848  */\r
9849 static void ScreenSquare(int column, int row, POINT * pt);\r
9850 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9851      POINT frames[], int * nFrames);\r
9852 \r
9853 \r
9854 #define kFactor 4\r
9855 \r
9856 void\r
9857 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9858 {       // [HGM] atomic: animate blast wave\r
9859         int i;\r
9860 \r
9861         explodeInfo.fromX = fromX;\r
9862         explodeInfo.fromY = fromY;\r
9863         explodeInfo.toX = toX;\r
9864         explodeInfo.toY = toY;\r
9865         for(i=1; i<4*kFactor; i++) {\r
9866             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9867             DrawPosition(FALSE, board);\r
9868             Sleep(appData.animSpeed);\r
9869         }\r
9870         explodeInfo.radius = 0;\r
9871         DrawPosition(TRUE, board);\r
9872 }\r
9873 \r
9874 void\r
9875 AnimateMove(board, fromX, fromY, toX, toY)\r
9876      Board board;\r
9877      int fromX;\r
9878      int fromY;\r
9879      int toX;\r
9880      int toY;\r
9881 {\r
9882   ChessSquare piece;\r
9883   POINT start, finish, mid;\r
9884   POINT frames[kFactor * 2 + 1];\r
9885   int nFrames, n;\r
9886 \r
9887   if (!appData.animate) return;\r
9888   if (doingSizing) return;\r
9889   if (fromY < 0 || fromX < 0) return;\r
9890   piece = board[fromY][fromX];\r
9891   if (piece >= EmptySquare) return;\r
9892 \r
9893   ScreenSquare(fromX, fromY, &start);\r
9894   ScreenSquare(toX, toY, &finish);\r
9895 \r
9896   /* All moves except knight jumps move in straight line */\r
9897   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9898     mid.x = start.x + (finish.x - start.x) / 2;\r
9899     mid.y = start.y + (finish.y - start.y) / 2;\r
9900   } else {\r
9901     /* Knight: make straight movement then diagonal */\r
9902     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9903        mid.x = start.x + (finish.x - start.x) / 2;\r
9904        mid.y = start.y;\r
9905      } else {\r
9906        mid.x = start.x;\r
9907        mid.y = start.y + (finish.y - start.y) / 2;\r
9908      }\r
9909   }\r
9910   \r
9911   /* Don't use as many frames for very short moves */\r
9912   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9913     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9914   else\r
9915     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9916 \r
9917   animInfo.from.x = fromX;\r
9918   animInfo.from.y = fromY;\r
9919   animInfo.to.x = toX;\r
9920   animInfo.to.y = toY;\r
9921   animInfo.lastpos = start;\r
9922   animInfo.piece = piece;\r
9923   for (n = 0; n < nFrames; n++) {\r
9924     animInfo.pos = frames[n];\r
9925     DrawPosition(FALSE, NULL);\r
9926     animInfo.lastpos = animInfo.pos;\r
9927     Sleep(appData.animSpeed);\r
9928   }\r
9929   animInfo.pos = finish;\r
9930   DrawPosition(FALSE, NULL);\r
9931   animInfo.piece = EmptySquare;\r
9932   Explode(board, fromX, fromY, toX, toY);\r
9933 }\r
9934 \r
9935 /*      Convert board position to corner of screen rect and color       */\r
9936 \r
9937 static void\r
9938 ScreenSquare(column, row, pt)\r
9939      int column; int row; POINT * pt;\r
9940 {\r
9941   if (flipView) {\r
9942     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9943     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9944   } else {\r
9945     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9946     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9947   }\r
9948 }\r
9949 \r
9950 /*      Generate a series of frame coords from start->mid->finish.\r
9951         The movement rate doubles until the half way point is\r
9952         reached, then halves back down to the final destination,\r
9953         which gives a nice slow in/out effect. The algorithmn\r
9954         may seem to generate too many intermediates for short\r
9955         moves, but remember that the purpose is to attract the\r
9956         viewers attention to the piece about to be moved and\r
9957         then to where it ends up. Too few frames would be less\r
9958         noticeable.                                             */\r
9959 \r
9960 static void\r
9961 Tween(start, mid, finish, factor, frames, nFrames)\r
9962      POINT * start; POINT * mid;\r
9963      POINT * finish; int factor;\r
9964      POINT frames[]; int * nFrames;\r
9965 {\r
9966   int n, fraction = 1, count = 0;\r
9967 \r
9968   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9969   for (n = 0; n < factor; n++)\r
9970     fraction *= 2;\r
9971   for (n = 0; n < factor; n++) {\r
9972     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9973     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9974     count ++;\r
9975     fraction = fraction / 2;\r
9976   }\r
9977   \r
9978   /* Midpoint */\r
9979   frames[count] = *mid;\r
9980   count ++;\r
9981   \r
9982   /* Slow out, stepping 1/2, then 1/4, ... */\r
9983   fraction = 2;\r
9984   for (n = 0; n < factor; n++) {\r
9985     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9986     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9987     count ++;\r
9988     fraction = fraction * 2;\r
9989   }\r
9990   *nFrames = count;\r
9991 }\r
9992 \r
9993 void\r
9994 SettingsPopUp(ChessProgramState *cps)\r
9995 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9996       EngineOptionsPopup(savedHwnd, cps);\r
9997 }\r
9998 \r
9999 int flock(int fid, int code)\r
10000 {\r
10001     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10002     OVERLAPPED ov;\r
10003     ov.hEvent = NULL;\r
10004     ov.Offset = 0;\r
10005     ov.OffsetHigh = 0;\r
10006     switch(code) {\r
10007       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10008       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10009       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10010       default: return -1;\r
10011     }\r
10012     return 0;\r
10013 }\r
10014 \r
10015 char *\r
10016 Col2Text (int n)\r
10017 {\r
10018     static int i=0;\r
10019     static char col[8][20];\r
10020     COLORREF color = *(COLORREF *) colorVariable[n];\r
10021     i = i+1 & 7;\r
10022     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10023     return col[i];\r
10024 }\r
10025 \r
10026 void\r
10027 ActivateTheme (int new)\r
10028 {   // Redo initialization of features depending on options that can occur in themes\r
10029    InitTextures();\r
10030    if(new) InitDrawingColors();\r
10031    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10032    InitDrawingSizes(-2, 0);\r
10033    InvalidateRect(hwndMain, NULL, TRUE);\r
10034 }\r