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