ba6933f7f419c2b40a9d82327a8b39087a52028b
[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, 2013, 2014, 2015 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 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 2, 0, 0 },\r
538   { "teeny",    25, 1, 1, 2, 0, 0 },\r
539   { "dinky",    29, 1, 1, 2, 0, 0 },\r
540   { "petite",   33, 1, 1, 2, 0, 0 },\r
541   { "slim",     37, 2, 1, 1, 0, 0 },\r
542   { "small",    40, 2, 1, 1, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
607   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
608 };\r
609 \r
610 \r
611 MySound sounds[(int)NSoundClasses];\r
612 MyTextAttribs textAttribs[(int)NColorClasses];\r
613 \r
614 MyColorizeAttribs colorizeAttribs[] = {\r
615   { (COLORREF)0, 0, N_("Shout Text") },\r
616   { (COLORREF)0, 0, N_("SShout/CShout") },\r
617   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
618   { (COLORREF)0, 0, N_("Channel Text") },\r
619   { (COLORREF)0, 0, N_("Kibitz Text") },\r
620   { (COLORREF)0, 0, N_("Tell Text") },\r
621   { (COLORREF)0, 0, N_("Challenge Text") },\r
622   { (COLORREF)0, 0, N_("Request Text") },\r
623   { (COLORREF)0, 0, N_("Seek Text") },\r
624   { (COLORREF)0, 0, N_("Normal Text") },\r
625   { (COLORREF)0, 0, N_("None") }\r
626 };\r
627 \r
628 \r
629 \r
630 static char *commentTitle;\r
631 static char *commentText;\r
632 static int commentIndex;\r
633 static Boolean editComment = FALSE;\r
634 \r
635 \r
636 char errorTitle[MSG_SIZ];\r
637 char errorMessage[2*MSG_SIZ];\r
638 HWND errorDialog = NULL;\r
639 BOOLEAN moveErrorMessageUp = FALSE;\r
640 BOOLEAN consoleEcho = TRUE;\r
641 CHARFORMAT consoleCF;\r
642 COLORREF consoleBackgroundColor;\r
643 \r
644 char *programVersion;\r
645 \r
646 #define CPReal 1\r
647 #define CPComm 2\r
648 #define CPSock 3\r
649 #define CPRcmd 4\r
650 typedef int CPKind;\r
651 \r
652 typedef struct {\r
653   CPKind kind;\r
654   HANDLE hProcess;\r
655   DWORD pid;\r
656   HANDLE hTo;\r
657   HANDLE hFrom;\r
658   SOCKET sock;\r
659   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
660 } ChildProc;\r
661 \r
662 #define INPUT_SOURCE_BUF_SIZE 4096\r
663 \r
664 typedef struct _InputSource {\r
665   CPKind kind;\r
666   HANDLE hFile;\r
667   SOCKET sock;\r
668   int lineByLine;\r
669   HANDLE hThread;\r
670   DWORD id;\r
671   char buf[INPUT_SOURCE_BUF_SIZE];\r
672   char *next;\r
673   DWORD count;\r
674   int error;\r
675   InputCallback func;\r
676   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
677   VOIDSTAR closure;\r
678 } InputSource;\r
679 \r
680 InputSource *consoleInputSource;\r
681 \r
682 DCB dcb;\r
683 \r
684 /* forward */\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
686 VOID ConsoleCreate();\r
687 LRESULT CALLBACK\r
688   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);\r
692 LRESULT CALLBACK\r
693   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
695 void ParseIcsTextMenu(char *icsTextMenuString);\r
696 VOID PopUpNameDialog(char firstchar);\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
698 \r
699 /* [AS] */\r
700 int NewGameFRC();\r
701 int GameListOptions();\r
702 \r
703 int dummy; // [HGM] for obsolete args\r
704 \r
705 HWND hwndMain = NULL;        /* root window*/\r
706 HWND hwndConsole = NULL;\r
707 HWND commentDialog = NULL;\r
708 HWND moveHistoryDialog = NULL;\r
709 HWND evalGraphDialog = NULL;\r
710 HWND engineOutputDialog = NULL;\r
711 HWND gameListDialog = NULL;\r
712 HWND editTagsDialog = NULL;\r
713 \r
714 int commentUp = FALSE;\r
715 \r
716 WindowPlacement wpMain;\r
717 WindowPlacement wpConsole;\r
718 WindowPlacement wpComment;\r
719 WindowPlacement wpMoveHistory;\r
720 WindowPlacement wpEvalGraph;\r
721 WindowPlacement wpEngineOutput;\r
722 WindowPlacement wpGameList;\r
723 WindowPlacement wpTags;\r
724 \r
725 VOID EngineOptionsPopup(); // [HGM] settings\r
726 \r
727 VOID GothicPopUp(char *title, VariantClass variant);\r
728 /*\r
729  * Setting "frozen" should disable all user input other than deleting\r
730  * the window.  We do this while engines are initializing themselves.\r
731  */\r
732 static int frozen = 0;\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];\r
734 void FreezeUI()\r
735 {\r
736   HMENU hmenu;\r
737   int i;\r
738 \r
739   if (frozen) return;\r
740   frozen = 1;\r
741   hmenu = GetMenu(hwndMain);\r
742   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
743     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
744   }\r
745   DrawMenuBar(hwndMain);\r
746 }\r
747 \r
748 /* Undo a FreezeUI */\r
749 void ThawUI()\r
750 {\r
751   HMENU hmenu;\r
752   int i;\r
753 \r
754   if (!frozen) return;\r
755   frozen = 0;\r
756   hmenu = GetMenu(hwndMain);\r
757   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
758     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
759   }\r
760   DrawMenuBar(hwndMain);\r
761 }\r
762 \r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
764 \r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
766 #ifdef JAWS\r
767 #include "jaws.c"\r
768 #else\r
769 #define JAWS_INIT\r
770 #define JAWS_ARGS\r
771 #define JAWS_ALT_INTERCEPT\r
772 #define JAWS_KBUP_NAVIGATION\r
773 #define JAWS_KBDOWN_NAVIGATION\r
774 #define JAWS_MENU_ITEMS\r
775 #define JAWS_SILENCE\r
776 #define JAWS_REPLAY\r
777 #define JAWS_ACCEL\r
778 #define JAWS_COPYRIGHT\r
779 #define JAWS_DELETE(X) X\r
780 #define SAYMACHINEMOVE()\r
781 #define SAY(X)\r
782 #endif\r
783 \r
784 /*---------------------------------------------------------------------------*\\r
785  *\r
786  * WinMain\r
787  *\r
788 \*---------------------------------------------------------------------------*/\r
789 \r
790 static void HandleMessage P((MSG *message));\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 \r
793 int APIENTRY\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
795         LPSTR lpCmdLine, int nCmdShow)\r
796 {\r
797   MSG msg;\r
798 //  INITCOMMONCONTROLSEX ex;\r
799 \r
800   debugFP = stderr;\r
801 \r
802   LoadLibrary("RICHED32.DLL");\r
803   consoleCF.cbSize = sizeof(CHARFORMAT);\r
804 \r
805   if (!InitApplication(hInstance)) {\r
806     return (FALSE);\r
807   }\r
808   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
809     return (FALSE);\r
810   }\r
811 \r
812   JAWS_INIT\r
813   TranslateMenus(1);\r
814 \r
815 //  InitCommonControlsEx(&ex);\r
816   InitCommonControls();\r
817 \r
818   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
819   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
820   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
821 \r
822   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
823 \r
824   while (GetMessage(&msg, /* message structure */\r
825                     NULL, /* handle of window receiving the message */\r
826                     0,    /* lowest message to examine */\r
827                     0))   /* highest message to examine */\r
828     {\r
829         HandleMessage(&msg);\r
830     }\r
831 \r
832 \r
833   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
834 }\r
835 \r
836 static void\r
837 HandleMessage (MSG *message)\r
838 {\r
839     MSG msg = *message;\r
840 \r
841       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
842         // [HGM] navigate: switch between all windows with tab\r
843         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
844         int i, currentElement = 0;\r
845 \r
846         // first determine what element of the chain we come from (if any)\r
847         if(appData.icsActive) {\r
848             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
849             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
850         }\r
851         if(engineOutputDialog && EngineOutputIsUp()) {\r
852             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
853             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
854         }\r
855         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
856             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
857         }\r
858         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
859         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
860         if(msg.hwnd == e1)                 currentElement = 2; else\r
861         if(msg.hwnd == e2)                 currentElement = 3; else\r
862         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
863         if(msg.hwnd == mh)                currentElement = 4; else\r
864         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
865         if(msg.hwnd == hText)  currentElement = 5; else\r
866         if(msg.hwnd == hInput) currentElement = 6; else\r
867         for (i = 0; i < N_BUTTONS; i++) {\r
868             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
869         }\r
870 \r
871         // determine where to go to\r
872         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
873           do {\r
874             currentElement = (currentElement + direction) % 7;\r
875             switch(currentElement) {\r
876                 case 0:\r
877                   h = hwndMain; break; // passing this case always makes the loop exit\r
878                 case 1:\r
879                   h = buttonDesc[0].hwnd; break; // could be NULL\r
880                 case 2:\r
881                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
882                   h = e1; break;\r
883                 case 3:\r
884                   if(!EngineOutputIsUp()) continue;\r
885                   h = e2; break;\r
886                 case 4:\r
887                   if(!MoveHistoryIsUp()) continue;\r
888                   h = mh; break;\r
889 //              case 6: // input to eval graph does not seem to get here!\r
890 //                if(!EvalGraphIsUp()) continue;\r
891 //                h = evalGraphDialog; break;\r
892                 case 5:\r
893                   if(!appData.icsActive) continue;\r
894                   SAY("display");\r
895                   h = hText; break;\r
896                 case 6:\r
897                   if(!appData.icsActive) continue;\r
898                   SAY("input");\r
899                   h = hInput; break;\r
900             }\r
901           } while(h == 0);\r
902 \r
903           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
904           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
905           SetFocus(h);\r
906 \r
907           return; // this message now has been processed\r
908         }\r
909       }\r
910 \r
911       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
912           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
913           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
914           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
915           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
916           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
917           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
918           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
921         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
922         for(i=0; i<MAX_CHAT; i++) \r
923             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
924                 done = 1; break;\r
925         }\r
926         if(done) return; // [HGM] chat: end patch\r
927         TranslateMessage(&msg); /* Translates virtual key codes */\r
928         DispatchMessage(&msg);  /* Dispatches message to window */\r
929       }\r
930 }\r
931 \r
932 void\r
933 DoEvents ()\r
934 { /* Dispatch pending messages */\r
935   MSG msg;\r
936   while (PeekMessage(&msg, /* message structure */\r
937                      NULL, /* handle of window receiving the message */\r
938                      0,    /* lowest message to examine */\r
939                      0,    /* highest message to examine */\r
940                      PM_REMOVE))\r
941     {\r
942         HandleMessage(&msg);\r
943     }\r
944 }\r
945 \r
946 /*---------------------------------------------------------------------------*\\r
947  *\r
948  * Initialization functions\r
949  *\r
950 \*---------------------------------------------------------------------------*/\r
951 \r
952 void\r
953 SetUserLogo()\r
954 {   // update user logo if necessary\r
955     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
956 \r
957     if(appData.autoLogo) {\r
958           curName = UserName();\r
959           if(strcmp(curName, oldUserName)) {\r
960                 GetCurrentDirectory(MSG_SIZ, dir);\r
961                 SetCurrentDirectory(installDir);\r
962                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
963                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
964                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
965                 if(userLogo == NULL)\r
966                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
967                 SetCurrentDirectory(dir); /* return to prev directory */\r
968           }\r
969     }\r
970 }\r
971 \r
972 BOOL\r
973 InitApplication(HINSTANCE hInstance)\r
974 {\r
975   WNDCLASS wc;\r
976 \r
977   /* Fill in window class structure with parameters that describe the */\r
978   /* main window. */\r
979 \r
980   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
981   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
982   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
983   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
984   wc.hInstance     = hInstance;         /* Owner of this class */\r
985   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
986   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
987   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
988   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
989   wc.lpszClassName = szAppName;                 /* Name to register as */\r
990 \r
991   /* Register the window class and return success/failure code. */\r
992   if (!RegisterClass(&wc)) return FALSE;\r
993 \r
994   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
995   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
996   wc.cbClsExtra    = 0;\r
997   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
998   wc.hInstance     = hInstance;\r
999   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1000   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1001   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1002   wc.lpszMenuName  = NULL;\r
1003   wc.lpszClassName = szConsoleName;\r
1004 \r
1005   if (!RegisterClass(&wc)) return FALSE;\r
1006   return TRUE;\r
1007 }\r
1008 \r
1009 \r
1010 /* Set by InitInstance, used by EnsureOnScreen */\r
1011 int screenHeight, screenWidth;\r
1012 RECT screenGeometry;\r
1013 \r
1014 void\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1016 {\r
1017 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1018   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1019   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1020   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1021   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1022   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1023 }\r
1024 \r
1025 VOID\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1027 {\r
1028   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1029   GetCurrentDirectory(MSG_SIZ, dir);\r
1030   SetCurrentDirectory(installDir);\r
1031   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1032       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1033 \r
1034       if (cps->programLogo == NULL && appData.debugMode) {\r
1035           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1036       }\r
1037   } else if(appData.autoLogo) {\r
1038       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1039         char *opponent = "";\r
1040         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1041         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1042         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1043         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1044             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1045             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046         }\r
1047       } else\r
1048       if(appData.directory[n] && appData.directory[n][0]) {\r
1049         SetCurrentDirectory(appData.directory[n]);\r
1050         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1051       }\r
1052   }\r
1053   SetCurrentDirectory(dir); /* return to prev directory */\r
1054 }\r
1055 \r
1056 VOID\r
1057 InitTextures()\r
1058 {\r
1059   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1060   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1061   \r
1062   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1063       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1064       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1065       liteBackTextureMode = appData.liteBackTextureMode;\r
1066 \r
1067       if (liteBackTexture == NULL && appData.debugMode) {\r
1068           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1069       }\r
1070   }\r
1071   \r
1072   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1073       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1074       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1075       darkBackTextureMode = appData.darkBackTextureMode;\r
1076 \r
1077       if (darkBackTexture == NULL && appData.debugMode) {\r
1078           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1079       }\r
1080   }\r
1081 }\r
1082 \r
1083 #ifndef SM_CXVIRTUALSCREEN\r
1084 #define SM_CXVIRTUALSCREEN 78\r
1085 #endif\r
1086 #ifndef SM_CYVIRTUALSCREEN\r
1087 #define SM_CYVIRTUALSCREEN 79\r
1088 #endif\r
1089 #ifndef SM_XVIRTUALSCREEN \r
1090 #define SM_XVIRTUALSCREEN 76\r
1091 #endif\r
1092 #ifndef SM_YVIRTUALSCREEN \r
1093 #define SM_YVIRTUALSCREEN 77\r
1094 #endif\r
1095 \r
1096 VOID\r
1097 InitGeometry()\r
1098 {\r
1099   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1100   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1101   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1102   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1103   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1104   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1105   screenGeometry.right = screenGeometry.left + screenWidth;\r
1106   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1107 }\r
1108 \r
1109 ChessProgramState broadcast;\r
1110 \r
1111 BOOL\r
1112 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1113 {\r
1114   HWND hwnd; /* Main window handle. */\r
1115   int ibs;\r
1116   WINDOWPLACEMENT wp;\r
1117   char *filepart;\r
1118 \r
1119   hInst = hInstance;    /* Store instance handle in our global variable */\r
1120   programName = szAppName;\r
1121 \r
1122   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1123     *filepart = NULLCHAR;\r
1124     SetCurrentDirectory(installDir);\r
1125   } else {\r
1126     GetCurrentDirectory(MSG_SIZ, installDir);\r
1127   }\r
1128   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1129   InitGeometry();\r
1130   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1131   /* xboard, and older WinBoards, controlled the move sound with the\r
1132      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1133      always turn the option on (so that the backend will call us),\r
1134      then let the user turn the sound off by setting it to silence if\r
1135      desired.  To accommodate old winboard.ini files saved by old\r
1136      versions of WinBoard, we also turn off the sound if the option\r
1137      was initially set to false. [HGM] taken out of InitAppData */\r
1138   if (!appData.ringBellAfterMoves) {\r
1139     sounds[(int)SoundMove].name = strdup("");\r
1140     appData.ringBellAfterMoves = TRUE;\r
1141   }\r
1142   if (appData.debugMode) {\r
1143     char *c = appData.nameOfDebugFile;\r
1144     if(strstr(c, "///") == c) {\r
1145       broadcast.which = "broadcaster";\r
1146       broadcast.pr   = NoProc;\r
1147       broadcast.isr  = NULL;\r
1148       broadcast.prog = c + 3;\r
1149       broadcast.dir  = ".";\r
1150       broadcast.host = "localhost";\r
1151       StartChessProgram(&broadcast);\r
1152       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1153     } else\r
1154     debugFP = fopen(c, "w");\r
1155     setbuf(debugFP, NULL);\r
1156   }\r
1157 \r
1158   LoadLanguageFile(appData.language);\r
1159 \r
1160   InitBackEnd1();\r
1161 \r
1162 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1163 //  InitEngineUCI( installDir, &second );\r
1164 \r
1165   /* Create a main window for this application instance. */\r
1166   hwnd = CreateWindow(szAppName, szTitle,\r
1167                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1168                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1169                       NULL, NULL, hInstance, NULL);\r
1170   hwndMain = hwnd;\r
1171 \r
1172   /* If window could not be created, return "failure" */\r
1173   if (!hwnd) {\r
1174     return (FALSE);\r
1175   }\r
1176 \r
1177   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1178   LoadLogo(&first, 0, FALSE);\r
1179   LoadLogo(&second, 1, appData.icsActive);\r
1180 \r
1181   SetUserLogo();\r
1182 \r
1183   iconWhite = LoadIcon(hInstance, "icon_white");\r
1184   iconBlack = LoadIcon(hInstance, "icon_black");\r
1185   iconCurrent = iconWhite;\r
1186   InitDrawingColors();\r
1187 \r
1188   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1189   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1190     /* Compute window size for each board size, and use the largest\r
1191        size that fits on this screen as the default. */\r
1192     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1193     if (boardSize == (BoardSize)-1 &&\r
1194         winH <= screenHeight\r
1195            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1196         && winW <= screenWidth) {\r
1197       boardSize = (BoardSize)ibs;\r
1198     }\r
1199   }\r
1200 \r
1201   InitDrawingSizes(boardSize, 0);\r
1202   RecentEngineMenu(appData.recentEngineList);\r
1203   InitMenuChecks();\r
1204   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1205 \r
1206   /* [AS] Load textures if specified */\r
1207   InitTextures();\r
1208 \r
1209   mysrandom( (unsigned) time(NULL) );\r
1210 \r
1211   /* [AS] Restore layout */\r
1212   if( wpMoveHistory.visible ) {\r
1213       MoveHistoryPopUp();\r
1214   }\r
1215 \r
1216   if( wpEvalGraph.visible ) {\r
1217       EvalGraphPopUp();\r
1218   }\r
1219 \r
1220   if( wpEngineOutput.visible ) {\r
1221       EngineOutputPopUp();\r
1222   }\r
1223 \r
1224   /* Make the window visible; update its client area; and return "success" */\r
1225   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1226   wp.length = sizeof(WINDOWPLACEMENT);\r
1227   wp.flags = 0;\r
1228   wp.showCmd = nCmdShow;\r
1229   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1230   wp.rcNormalPosition.left = wpMain.x;\r
1231   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1232   wp.rcNormalPosition.top = wpMain.y;\r
1233   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1234   SetWindowPlacement(hwndMain, &wp);\r
1235 \r
1236   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1237 \r
1238   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1239                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1240 \r
1241   if (hwndConsole) {\r
1242 #if AOT_CONSOLE\r
1243     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1244                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1245 #endif\r
1246     ShowWindow(hwndConsole, nCmdShow);\r
1247     SetActiveWindow(hwndConsole);\r
1248   }\r
1249   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1250   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1251 \r
1252   return TRUE;\r
1253 \r
1254 }\r
1255 \r
1256 VOID\r
1257 InitMenuChecks()\r
1258 {\r
1259   HMENU hmenu = GetMenu(hwndMain);\r
1260 \r
1261   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1262                         MF_BYCOMMAND|((appData.icsActive &&\r
1263                                        *appData.icsCommPort != NULLCHAR) ?\r
1264                                       MF_ENABLED : MF_GRAYED));\r
1265   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1266                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1267                                      MF_CHECKED : MF_UNCHECKED));\r
1268   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1269 }\r
1270 \r
1271 //---------------------------------------------------------------------------------------------------------\r
1272 \r
1273 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1274 #define XBOARD FALSE\r
1275 \r
1276 #define OPTCHAR "/"\r
1277 #define SEPCHAR "="\r
1278 #define TOPLEVEL 0\r
1279 \r
1280 #include "args.h"\r
1281 \r
1282 // front-end part of option handling\r
1283 \r
1284 VOID\r
1285 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1286 {\r
1287   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1288   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1289   DeleteDC(hdc);\r
1290   lf->lfWidth = 0;\r
1291   lf->lfEscapement = 0;\r
1292   lf->lfOrientation = 0;\r
1293   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1294   lf->lfItalic = mfp->italic;\r
1295   lf->lfUnderline = mfp->underline;\r
1296   lf->lfStrikeOut = mfp->strikeout;\r
1297   lf->lfCharSet = mfp->charset;\r
1298   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1299 \r
1300 \r
1301 \r
1302   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1303   lf->lfQuality = DEFAULT_QUALITY;\r
1304   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1305     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1306 }\r
1307 \r
1308 void\r
1309 CreateFontInMF(MyFont *mf)\r
1310\r
1311   LFfromMFP(&mf->lf, &mf->mfp);\r
1312   if (mf->hf) DeleteObject(mf->hf);\r
1313   mf->hf = CreateFontIndirect(&mf->lf);\r
1314 }\r
1315 \r
1316 // [HGM] This platform-dependent table provides the location for storing the color info\r
1317 void *\r
1318 colorVariable[] = {\r
1319   &whitePieceColor, \r
1320   &blackPieceColor, \r
1321   &lightSquareColor,\r
1322   &darkSquareColor, \r
1323   &highlightSquareColor,\r
1324   &premoveHighlightColor,\r
1325   NULL,\r
1326   &consoleBackgroundColor,\r
1327   &appData.fontForeColorWhite,\r
1328   &appData.fontBackColorWhite,\r
1329   &appData.fontForeColorBlack,\r
1330   &appData.fontBackColorBlack,\r
1331   &appData.evalHistColorWhite,\r
1332   &appData.evalHistColorBlack,\r
1333   &appData.highlightArrowColor,\r
1334 };\r
1335 \r
1336 /* Command line font name parser.  NULL name means do nothing.\r
1337    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1338    For backward compatibility, syntax without the colon is also\r
1339    accepted, but font names with digits in them won't work in that case.\r
1340 */\r
1341 VOID\r
1342 ParseFontName(char *name, MyFontParams *mfp)\r
1343 {\r
1344   char *p, *q;\r
1345   if (name == NULL) return;\r
1346   p = name;\r
1347   q = strchr(p, ':');\r
1348   if (q) {\r
1349     if (q - p >= sizeof(mfp->faceName))\r
1350       ExitArgError(_("Font name too long:"), name, TRUE);\r
1351     memcpy(mfp->faceName, p, q - p);\r
1352     mfp->faceName[q - p] = NULLCHAR;\r
1353     p = q + 1;\r
1354   } else {\r
1355     q = mfp->faceName;\r
1356 \r
1357     while (*p && !isdigit(*p)) {\r
1358       *q++ = *p++;\r
1359       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1360         ExitArgError(_("Font name too long:"), name, TRUE);\r
1361     }\r
1362     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1363     *q = NULLCHAR;\r
1364   }\r
1365   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1366   mfp->pointSize = (float) atof(p);\r
1367   mfp->bold = (strchr(p, 'b') != NULL);\r
1368   mfp->italic = (strchr(p, 'i') != NULL);\r
1369   mfp->underline = (strchr(p, 'u') != NULL);\r
1370   mfp->strikeout = (strchr(p, 's') != NULL);\r
1371   mfp->charset = DEFAULT_CHARSET;\r
1372   q = strchr(p, 'c');\r
1373   if (q)\r
1374     mfp->charset = (BYTE) atoi(q+1);\r
1375 }\r
1376 \r
1377 void\r
1378 ParseFont(char *name, int number)\r
1379 { // wrapper to shield back-end from 'font'\r
1380   ParseFontName(name, &font[boardSize][number]->mfp);\r
1381 }\r
1382 \r
1383 void\r
1384 SetFontDefaults()\r
1385 { // in WB  we have a 2D array of fonts; this initializes their description\r
1386   int i, j;\r
1387   /* Point font array elements to structures and\r
1388      parse default font names */\r
1389   for (i=0; i<NUM_FONTS; i++) {\r
1390     for (j=0; j<NUM_SIZES; j++) {\r
1391       font[j][i] = &fontRec[j][i];\r
1392       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1393     }\r
1394   }\r
1395 }\r
1396 \r
1397 void\r
1398 CreateFonts()\r
1399 { // here we create the actual fonts from the selected descriptions\r
1400   int i, j;\r
1401   for (i=0; i<NUM_FONTS; i++) {\r
1402     for (j=0; j<NUM_SIZES; j++) {\r
1403       CreateFontInMF(font[j][i]);\r
1404     }\r
1405   }\r
1406 }\r
1407 /* Color name parser.\r
1408    X version accepts X color names, but this one\r
1409    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1410 COLORREF\r
1411 ParseColorName(char *name)\r
1412 {\r
1413   int red, green, blue, count;\r
1414   char buf[MSG_SIZ];\r
1415 \r
1416   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1417   if (count != 3) {\r
1418     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1419       &red, &green, &blue);\r
1420   }\r
1421   if (count != 3) {\r
1422     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1423     DisplayError(buf, 0);\r
1424     return RGB(0, 0, 0);\r
1425   }\r
1426   return PALETTERGB(red, green, blue);\r
1427 }\r
1428 \r
1429 void\r
1430 ParseColor(int n, char *name)\r
1431 { // for WinBoard the color is an int, which needs to be derived from the string\r
1432   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1433 }\r
1434 \r
1435 void\r
1436 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1437 {\r
1438   char *e = argValue;\r
1439   int eff = 0;\r
1440 \r
1441   while (*e) {\r
1442     if (*e == 'b')      eff |= CFE_BOLD;\r
1443     else if (*e == 'i') eff |= CFE_ITALIC;\r
1444     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1445     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1446     else if (*e == '#' || isdigit(*e)) break;\r
1447     e++;\r
1448   }\r
1449   *effects = eff;\r
1450   *color   = ParseColorName(e);\r
1451 }\r
1452 \r
1453 void\r
1454 ParseTextAttribs(ColorClass cc, char *s)\r
1455 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1456     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1457     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseBoardSize(void *addr, char *name)\r
1462 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1463   BoardSize bs = SizeTiny;\r
1464   while (sizeInfo[bs].name != NULL) {\r
1465     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1466         *(BoardSize *)addr = bs;\r
1467         return;\r
1468     }\r
1469     bs++;\r
1470   }\r
1471   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1472 }\r
1473 \r
1474 void\r
1475 LoadAllSounds()\r
1476 { // [HGM] import name from appData first\r
1477   ColorClass cc;\r
1478   SoundClass sc;\r
1479   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1480     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1481     textAttribs[cc].sound.data = NULL;\r
1482     MyLoadSound(&textAttribs[cc].sound);\r
1483   }\r
1484   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1485     textAttribs[cc].sound.name = strdup("");\r
1486     textAttribs[cc].sound.data = NULL;\r
1487   }\r
1488   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1489     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1490     sounds[sc].data = NULL;\r
1491     MyLoadSound(&sounds[sc]);\r
1492   }\r
1493 }\r
1494 \r
1495 void\r
1496 SetCommPortDefaults()\r
1497 {\r
1498    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1499   dcb.DCBlength = sizeof(DCB);\r
1500   dcb.BaudRate = 9600;\r
1501   dcb.fBinary = TRUE;\r
1502   dcb.fParity = FALSE;\r
1503   dcb.fOutxCtsFlow = FALSE;\r
1504   dcb.fOutxDsrFlow = FALSE;\r
1505   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1506   dcb.fDsrSensitivity = FALSE;\r
1507   dcb.fTXContinueOnXoff = TRUE;\r
1508   dcb.fOutX = FALSE;\r
1509   dcb.fInX = FALSE;\r
1510   dcb.fNull = FALSE;\r
1511   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1512   dcb.fAbortOnError = FALSE;\r
1513   dcb.ByteSize = 7;\r
1514   dcb.Parity = SPACEPARITY;\r
1515   dcb.StopBits = ONESTOPBIT;\r
1516 }\r
1517 \r
1518 // [HGM] args: these three cases taken out to stay in front-end\r
1519 void\r
1520 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1521 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1522         // while the curent board size determines the element. This system should be ported to XBoard.\r
1523         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1524         int bs;\r
1525         for (bs=0; bs<NUM_SIZES; bs++) {\r
1526           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1527           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1528           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1529             ad->argName, mfp->faceName, mfp->pointSize,\r
1530             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1531             mfp->bold ? "b" : "",\r
1532             mfp->italic ? "i" : "",\r
1533             mfp->underline ? "u" : "",\r
1534             mfp->strikeout ? "s" : "",\r
1535             (int)mfp->charset);\r
1536         }\r
1537       }\r
1538 \r
1539 void\r
1540 ExportSounds()\r
1541 { // [HGM] copy the names from the internal WB variables to appData\r
1542   ColorClass cc;\r
1543   SoundClass sc;\r
1544   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1545     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1546   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1547     (&appData.soundMove)[sc] = sounds[sc].name;\r
1548 }\r
1549 \r
1550 void\r
1551 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1552 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1553         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1554         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1555           (ta->effects & CFE_BOLD) ? "b" : "",\r
1556           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1557           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1558           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1559           (ta->effects) ? " " : "",\r
1560           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1561       }\r
1562 \r
1563 void\r
1564 SaveColor(FILE *f, ArgDescriptor *ad)\r
1565 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1566         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1567         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1568           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1569 }\r
1570 \r
1571 void\r
1572 SaveBoardSize(FILE *f, char *name, void *addr)\r
1573 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1574   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1575 }\r
1576 \r
1577 void\r
1578 ParseCommPortSettings(char *s)\r
1579 { // wrapper to keep dcb from back-end\r
1580   ParseCommSettings(s, &dcb);\r
1581 }\r
1582 \r
1583 void\r
1584 GetWindowCoords()\r
1585 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1586   GetActualPlacement(hwndMain, &wpMain);\r
1587   GetActualPlacement(hwndConsole, &wpConsole);\r
1588   GetActualPlacement(commentDialog, &wpComment);\r
1589   GetActualPlacement(editTagsDialog, &wpTags);\r
1590   GetActualPlacement(gameListDialog, &wpGameList);\r
1591   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1592   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1593   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1594 }\r
1595 \r
1596 void\r
1597 PrintCommPortSettings(FILE *f, char *name)\r
1598 { // wrapper to shield back-end from DCB\r
1599       PrintCommSettings(f, name, &dcb);\r
1600 }\r
1601 \r
1602 int\r
1603 MySearchPath(char *installDir, char *name, char *fullname)\r
1604 {\r
1605   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1606   if(name[0]== '%') {\r
1607     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1608     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1609       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1610       *strchr(buf, '%') = 0;\r
1611       strcat(fullname, getenv(buf));\r
1612       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1613     }\r
1614     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1615     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1616     return (int) strlen(fullname);\r
1617   }\r
1618   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1619 }\r
1620 \r
1621 int\r
1622 MyGetFullPathName(char *name, char *fullname)\r
1623 {\r
1624   char *dummy;\r
1625   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1626 }\r
1627 \r
1628 int\r
1629 MainWindowUp()\r
1630 { // [HGM] args: allows testing if main window is realized from back-end\r
1631   return hwndMain != NULL;\r
1632 }\r
1633 \r
1634 void\r
1635 PopUpStartupDialog()\r
1636 {\r
1637     FARPROC lpProc;\r
1638     \r
1639     LoadLanguageFile(appData.language);\r
1640     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1641     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1642     FreeProcInstance(lpProc);\r
1643 }\r
1644 \r
1645 /*---------------------------------------------------------------------------*\\r
1646  *\r
1647  * GDI board drawing routines\r
1648  *\r
1649 \*---------------------------------------------------------------------------*/\r
1650 \r
1651 /* [AS] Draw square using background texture */\r
1652 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1653 {\r
1654     XFORM   x;\r
1655 \r
1656     if( mode == 0 ) {\r
1657         return; /* Should never happen! */\r
1658     }\r
1659 \r
1660     SetGraphicsMode( dst, GM_ADVANCED );\r
1661 \r
1662     switch( mode ) {\r
1663     case 1:\r
1664         /* Identity */\r
1665         break;\r
1666     case 2:\r
1667         /* X reflection */\r
1668         x.eM11 = -1.0;\r
1669         x.eM12 = 0;\r
1670         x.eM21 = 0;\r
1671         x.eM22 = 1.0;\r
1672         x.eDx = (FLOAT) dw + dx - 1;\r
1673         x.eDy = 0;\r
1674         dx = 0;\r
1675         SetWorldTransform( dst, &x );\r
1676         break;\r
1677     case 3:\r
1678         /* Y reflection */\r
1679         x.eM11 = 1.0;\r
1680         x.eM12 = 0;\r
1681         x.eM21 = 0;\r
1682         x.eM22 = -1.0;\r
1683         x.eDx = 0;\r
1684         x.eDy = (FLOAT) dh + dy - 1;\r
1685         dy = 0;\r
1686         SetWorldTransform( dst, &x );\r
1687         break;\r
1688     case 4:\r
1689         /* X/Y flip */\r
1690         x.eM11 = 0;\r
1691         x.eM12 = 1.0;\r
1692         x.eM21 = 1.0;\r
1693         x.eM22 = 0;\r
1694         x.eDx = (FLOAT) dx;\r
1695         x.eDy = (FLOAT) dy;\r
1696         dx = 0;\r
1697         dy = 0;\r
1698         SetWorldTransform( dst, &x );\r
1699         break;\r
1700     }\r
1701 \r
1702     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1703 \r
1704     x.eM11 = 1.0;\r
1705     x.eM12 = 0;\r
1706     x.eM21 = 0;\r
1707     x.eM22 = 1.0;\r
1708     x.eDx = 0;\r
1709     x.eDy = 0;\r
1710     SetWorldTransform( dst, &x );\r
1711 \r
1712     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1713 }\r
1714 \r
1715 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1716 enum {\r
1717     PM_WP = (int) WhitePawn, \r
1718     PM_WN = (int) WhiteKnight, \r
1719     PM_WB = (int) WhiteBishop, \r
1720     PM_WR = (int) WhiteRook, \r
1721     PM_WQ = (int) WhiteQueen, \r
1722     PM_WF = (int) WhiteFerz, \r
1723     PM_WW = (int) WhiteWazir, \r
1724     PM_WE = (int) WhiteAlfil, \r
1725     PM_WM = (int) WhiteMan, \r
1726     PM_WO = (int) WhiteCannon, \r
1727     PM_WU = (int) WhiteUnicorn, \r
1728     PM_WH = (int) WhiteNightrider, \r
1729     PM_WA = (int) WhiteAngel, \r
1730     PM_WC = (int) WhiteMarshall, \r
1731     PM_WAB = (int) WhiteCardinal, \r
1732     PM_WD = (int) WhiteDragon, \r
1733     PM_WL = (int) WhiteLance, \r
1734     PM_WS = (int) WhiteCobra, \r
1735     PM_WV = (int) WhiteFalcon, \r
1736     PM_WSG = (int) WhiteSilver, \r
1737     PM_WG = (int) WhiteGrasshopper, \r
1738     PM_WK = (int) WhiteKing,\r
1739     PM_BP = (int) BlackPawn, \r
1740     PM_BN = (int) BlackKnight, \r
1741     PM_BB = (int) BlackBishop, \r
1742     PM_BR = (int) BlackRook, \r
1743     PM_BQ = (int) BlackQueen, \r
1744     PM_BF = (int) BlackFerz, \r
1745     PM_BW = (int) BlackWazir, \r
1746     PM_BE = (int) BlackAlfil, \r
1747     PM_BM = (int) BlackMan,\r
1748     PM_BO = (int) BlackCannon, \r
1749     PM_BU = (int) BlackUnicorn, \r
1750     PM_BH = (int) BlackNightrider, \r
1751     PM_BA = (int) BlackAngel, \r
1752     PM_BC = (int) BlackMarshall, \r
1753     PM_BG = (int) BlackGrasshopper, \r
1754     PM_BAB = (int) BlackCardinal,\r
1755     PM_BD = (int) BlackDragon,\r
1756     PM_BL = (int) BlackLance,\r
1757     PM_BS = (int) BlackCobra,\r
1758     PM_BV = (int) BlackFalcon,\r
1759     PM_BSG = (int) BlackSilver,\r
1760     PM_BK = (int) BlackKing\r
1761 };\r
1762 \r
1763 static HFONT hPieceFont = NULL;\r
1764 static HBITMAP hPieceMask[(int) EmptySquare];\r
1765 static HBITMAP hPieceFace[(int) EmptySquare];\r
1766 static int fontBitmapSquareSize = 0;\r
1767 static char pieceToFontChar[(int) EmptySquare] =\r
1768                               { 'p', 'n', 'b', 'r', 'q', \r
1769                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1770                       'k', 'o', 'm', 'v', 't', 'w', \r
1771                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1772                                                               'l' };\r
1773 \r
1774 extern BOOL SetCharTable( char *table, const char * map );\r
1775 /* [HGM] moved to backend.c */\r
1776 \r
1777 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1778 {\r
1779     HBRUSH hbrush;\r
1780     BYTE r1 = GetRValue( color );\r
1781     BYTE g1 = GetGValue( color );\r
1782     BYTE b1 = GetBValue( color );\r
1783     BYTE r2 = r1 / 2;\r
1784     BYTE g2 = g1 / 2;\r
1785     BYTE b2 = b1 / 2;\r
1786     RECT rc;\r
1787 \r
1788     /* Create a uniform background first */\r
1789     hbrush = CreateSolidBrush( color );\r
1790     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1791     FillRect( hdc, &rc, hbrush );\r
1792     DeleteObject( hbrush );\r
1793     \r
1794     if( mode == 1 ) {\r
1795         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1796         int steps = squareSize / 2;\r
1797         int i;\r
1798 \r
1799         for( i=0; i<steps; i++ ) {\r
1800             BYTE r = r1 - (r1-r2) * i / steps;\r
1801             BYTE g = g1 - (g1-g2) * i / steps;\r
1802             BYTE b = b1 - (b1-b2) * i / steps;\r
1803 \r
1804             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1805             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1806             FillRect( hdc, &rc, hbrush );\r
1807             DeleteObject(hbrush);\r
1808         }\r
1809     }\r
1810     else if( mode == 2 ) {\r
1811         /* Diagonal gradient, good more or less for every piece */\r
1812         POINT triangle[3];\r
1813         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1814         HBRUSH hbrush_old;\r
1815         int steps = squareSize;\r
1816         int i;\r
1817 \r
1818         triangle[0].x = squareSize - steps;\r
1819         triangle[0].y = squareSize;\r
1820         triangle[1].x = squareSize;\r
1821         triangle[1].y = squareSize;\r
1822         triangle[2].x = squareSize;\r
1823         triangle[2].y = squareSize - steps;\r
1824 \r
1825         for( i=0; i<steps; i++ ) {\r
1826             BYTE r = r1 - (r1-r2) * i / steps;\r
1827             BYTE g = g1 - (g1-g2) * i / steps;\r
1828             BYTE b = b1 - (b1-b2) * i / steps;\r
1829 \r
1830             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1831             hbrush_old = SelectObject( hdc, hbrush );\r
1832             Polygon( hdc, triangle, 3 );\r
1833             SelectObject( hdc, hbrush_old );\r
1834             DeleteObject(hbrush);\r
1835             triangle[0].x++;\r
1836             triangle[2].y++;\r
1837         }\r
1838 \r
1839         SelectObject( hdc, hpen );\r
1840     }\r
1841 }\r
1842 \r
1843 /*\r
1844     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1845     seems to work ok. The main problem here is to find the "inside" of a chess\r
1846     piece: follow the steps as explained below.\r
1847 */\r
1848 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1849 {\r
1850     HBITMAP hbm;\r
1851     HBITMAP hbm_old;\r
1852     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1853     RECT rc;\r
1854     SIZE sz;\r
1855 \r
1856 \r
1857     POINT pt;\r
1858     int backColor = whitePieceColor; \r
1859     int foreColor = blackPieceColor;\r
1860     \r
1861     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1862         backColor = appData.fontBackColorWhite;\r
1863         foreColor = appData.fontForeColorWhite;\r
1864     }\r
1865     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1866         backColor = appData.fontBackColorBlack;\r
1867         foreColor = appData.fontForeColorBlack;\r
1868     }\r
1869 \r
1870     /* Mask */\r
1871     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873     hbm_old = SelectObject( hdc, hbm );\r
1874 \r
1875     rc.left = 0;\r
1876     rc.top = 0;\r
1877     rc.right = squareSize;\r
1878     rc.bottom = squareSize;\r
1879 \r
1880     /* Step 1: background is now black */\r
1881     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1882 \r
1883     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1884 \r
1885     pt.x = (squareSize - sz.cx) / 2;\r
1886     pt.y = (squareSize - sz.cy) / 2;\r
1887 \r
1888     SetBkMode( hdc, TRANSPARENT );\r
1889     SetTextColor( hdc, chroma );\r
1890     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1891     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1892 \r
1893     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1894     /* Step 3: the area outside the piece is filled with white */\r
1895 //    FloodFill( hdc, 0, 0, chroma );\r
1896     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1897     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1898     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1899     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1900     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1901     /* \r
1902         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1903         but if the start point is not inside the piece we're lost!\r
1904         There should be a better way to do this... if we could create a region or path\r
1905         from the fill operation we would be fine for example.\r
1906     */\r
1907 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1908     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1909 \r
1910     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1911         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1912         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1913 \r
1914         SelectObject( dc2, bm2 );\r
1915         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1916         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1917         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1918         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1919         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1920 \r
1921         DeleteDC( dc2 );\r
1922         DeleteObject( bm2 );\r
1923     }\r
1924 \r
1925     SetTextColor( hdc, 0 );\r
1926     /* \r
1927         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1928         draw the piece again in black for safety.\r
1929     */\r
1930     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1931 \r
1932     SelectObject( hdc, hbm_old );\r
1933 \r
1934     if( hPieceMask[index] != NULL ) {\r
1935         DeleteObject( hPieceMask[index] );\r
1936     }\r
1937 \r
1938     hPieceMask[index] = hbm;\r
1939 \r
1940     /* Face */\r
1941     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1942 \r
1943     SelectObject( hdc, hbm );\r
1944 \r
1945     {\r
1946         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1947         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1948         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1949 \r
1950         SelectObject( dc1, hPieceMask[index] );\r
1951         SelectObject( dc2, bm2 );\r
1952         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1953         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1954         \r
1955         /* \r
1956             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1957             the piece background and deletes (makes transparent) the rest.\r
1958             Thanks to that mask, we are free to paint the background with the greates\r
1959             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1960             We use this, to make gradients and give the pieces a "roundish" look.\r
1961         */\r
1962         SetPieceBackground( hdc, backColor, 2 );\r
1963         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1964 \r
1965         DeleteDC( dc2 );\r
1966         DeleteDC( dc1 );\r
1967         DeleteObject( bm2 );\r
1968     }\r
1969 \r
1970     SetTextColor( hdc, foreColor );\r
1971     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1972 \r
1973     SelectObject( hdc, hbm_old );\r
1974 \r
1975     if( hPieceFace[index] != NULL ) {\r
1976         DeleteObject( hPieceFace[index] );\r
1977     }\r
1978 \r
1979     hPieceFace[index] = hbm;\r
1980 }\r
1981 \r
1982 static int TranslatePieceToFontPiece( int piece )\r
1983 {\r
1984     switch( piece ) {\r
1985     case BlackPawn:\r
1986         return PM_BP;\r
1987     case BlackKnight:\r
1988         return PM_BN;\r
1989     case BlackBishop:\r
1990         return PM_BB;\r
1991     case BlackRook:\r
1992         return PM_BR;\r
1993     case BlackQueen:\r
1994         return PM_BQ;\r
1995     case BlackKing:\r
1996         return PM_BK;\r
1997     case WhitePawn:\r
1998         return PM_WP;\r
1999     case WhiteKnight:\r
2000         return PM_WN;\r
2001     case WhiteBishop:\r
2002         return PM_WB;\r
2003     case WhiteRook:\r
2004         return PM_WR;\r
2005     case WhiteQueen:\r
2006         return PM_WQ;\r
2007     case WhiteKing:\r
2008         return PM_WK;\r
2009 \r
2010     case BlackAngel:\r
2011         return PM_BA;\r
2012     case BlackMarshall:\r
2013         return PM_BC;\r
2014     case BlackFerz:\r
2015         return PM_BF;\r
2016     case BlackNightrider:\r
2017         return PM_BH;\r
2018     case BlackAlfil:\r
2019         return PM_BE;\r
2020     case BlackWazir:\r
2021         return PM_BW;\r
2022     case BlackUnicorn:\r
2023         return PM_BU;\r
2024     case BlackCannon:\r
2025         return PM_BO;\r
2026     case BlackGrasshopper:\r
2027         return PM_BG;\r
2028     case BlackMan:\r
2029         return PM_BM;\r
2030     case BlackSilver:\r
2031         return PM_BSG;\r
2032     case BlackLance:\r
2033         return PM_BL;\r
2034     case BlackFalcon:\r
2035         return PM_BV;\r
2036     case BlackCobra:\r
2037         return PM_BS;\r
2038     case BlackCardinal:\r
2039         return PM_BAB;\r
2040     case BlackDragon:\r
2041         return PM_BD;\r
2042 \r
2043     case WhiteAngel:\r
2044         return PM_WA;\r
2045     case WhiteMarshall:\r
2046         return PM_WC;\r
2047     case WhiteFerz:\r
2048         return PM_WF;\r
2049     case WhiteNightrider:\r
2050         return PM_WH;\r
2051     case WhiteAlfil:\r
2052         return PM_WE;\r
2053     case WhiteWazir:\r
2054         return PM_WW;\r
2055     case WhiteUnicorn:\r
2056         return PM_WU;\r
2057     case WhiteCannon:\r
2058         return PM_WO;\r
2059     case WhiteGrasshopper:\r
2060         return PM_WG;\r
2061     case WhiteMan:\r
2062         return PM_WM;\r
2063     case WhiteSilver:\r
2064         return PM_WSG;\r
2065     case WhiteLance:\r
2066         return PM_WL;\r
2067     case WhiteFalcon:\r
2068         return PM_WV;\r
2069     case WhiteCobra:\r
2070         return PM_WS;\r
2071     case WhiteCardinal:\r
2072         return PM_WAB;\r
2073     case WhiteDragon:\r
2074         return PM_WD;\r
2075     }\r
2076 \r
2077     return 0;\r
2078 }\r
2079 \r
2080 void CreatePiecesFromFont()\r
2081 {\r
2082     LOGFONT lf;\r
2083     HDC hdc_window = NULL;\r
2084     HDC hdc = NULL;\r
2085     HFONT hfont_old;\r
2086     int fontHeight;\r
2087     int i;\r
2088 \r
2089     if( fontBitmapSquareSize < 0 ) {\r
2090         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2091         return;\r
2092     }\r
2093 \r
2094     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2095             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2096         fontBitmapSquareSize = -1;\r
2097         return;\r
2098     }\r
2099 \r
2100     if( fontBitmapSquareSize != squareSize ) {\r
2101         hdc_window = GetDC( hwndMain );\r
2102         hdc = CreateCompatibleDC( hdc_window );\r
2103 \r
2104         if( hPieceFont != NULL ) {\r
2105             DeleteObject( hPieceFont );\r
2106         }\r
2107         else {\r
2108             for( i=0; i<=(int)BlackKing; i++ ) {\r
2109                 hPieceMask[i] = NULL;\r
2110                 hPieceFace[i] = NULL;\r
2111             }\r
2112         }\r
2113 \r
2114         fontHeight = 75;\r
2115 \r
2116         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2117             fontHeight = appData.fontPieceSize;\r
2118         }\r
2119 \r
2120         fontHeight = (fontHeight * squareSize) / 100;\r
2121 \r
2122         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2123         lf.lfWidth = 0;\r
2124         lf.lfEscapement = 0;\r
2125         lf.lfOrientation = 0;\r
2126         lf.lfWeight = FW_NORMAL;\r
2127         lf.lfItalic = 0;\r
2128         lf.lfUnderline = 0;\r
2129         lf.lfStrikeOut = 0;\r
2130         lf.lfCharSet = DEFAULT_CHARSET;\r
2131         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2132         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2133         lf.lfQuality = PROOF_QUALITY;\r
2134         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2135         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2136         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2137 \r
2138         hPieceFont = CreateFontIndirect( &lf );\r
2139 \r
2140         if( hPieceFont == NULL ) {\r
2141             fontBitmapSquareSize = -2;\r
2142         }\r
2143         else {\r
2144             /* Setup font-to-piece character table */\r
2145             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2146                 /* No (or wrong) global settings, try to detect the font */\r
2147                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2148                     /* Alpha */\r
2149                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2150                 }\r
2151                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2152                     /* DiagramTT* family */\r
2153                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2154                 }\r
2155                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2156                     /* Fairy symbols */\r
2157                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2158                 }\r
2159                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2160                     /* Good Companion (Some characters get warped as literal :-( */\r
2161                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2162                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2163                     SetCharTable(pieceToFontChar, s);\r
2164                 }\r
2165                 else {\r
2166                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2167                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2168                 }\r
2169             }\r
2170 \r
2171             /* Create bitmaps */\r
2172             hfont_old = SelectObject( hdc, hPieceFont );\r
2173             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2174                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2175                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2176 \r
2177             SelectObject( hdc, hfont_old );\r
2178 \r
2179             fontBitmapSquareSize = squareSize;\r
2180         }\r
2181     }\r
2182 \r
2183     if( hdc != NULL ) {\r
2184         DeleteDC( hdc );\r
2185     }\r
2186 \r
2187     if( hdc_window != NULL ) {\r
2188         ReleaseDC( hwndMain, hdc_window );\r
2189     }\r
2190 }\r
2191 \r
2192 HBITMAP\r
2193 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2194 {\r
2195   char name[128], buf[MSG_SIZ];\r
2196 \r
2197     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2198   if(appData.pieceDirectory[0]) {\r
2199     HBITMAP res;\r
2200     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2201     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2202     if(res) return res;\r
2203   }\r
2204   if (gameInfo.event &&\r
2205       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2206       strcmp(name, "k80s") == 0) {\r
2207     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2208   }\r
2209   return LoadBitmap(hinst, name);\r
2210 }\r
2211 \r
2212 \r
2213 /* Insert a color into the program's logical palette\r
2214    structure.  This code assumes the given color is\r
2215    the result of the RGB or PALETTERGB macro, and it\r
2216    knows how those macros work (which is documented).\r
2217 */\r
2218 VOID\r
2219 InsertInPalette(COLORREF color)\r
2220 {\r
2221   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2222 \r
2223   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2224     DisplayFatalError(_("Too many colors"), 0, 1);\r
2225     pLogPal->palNumEntries--;\r
2226     return;\r
2227   }\r
2228 \r
2229   pe->peFlags = (char) 0;\r
2230   pe->peRed = (char) (0xFF & color);\r
2231   pe->peGreen = (char) (0xFF & (color >> 8));\r
2232   pe->peBlue = (char) (0xFF & (color >> 16));\r
2233   return;\r
2234 }\r
2235 \r
2236 \r
2237 VOID\r
2238 InitDrawingColors()\r
2239 {\r
2240   int i;\r
2241   if (pLogPal == NULL) {\r
2242     /* Allocate enough memory for a logical palette with\r
2243      * PALETTESIZE entries and set the size and version fields\r
2244      * of the logical palette structure.\r
2245      */\r
2246     pLogPal = (NPLOGPALETTE)\r
2247       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2248                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2249     pLogPal->palVersion    = 0x300;\r
2250   }\r
2251   pLogPal->palNumEntries = 0;\r
2252 \r
2253   InsertInPalette(lightSquareColor);\r
2254   InsertInPalette(darkSquareColor);\r
2255   InsertInPalette(whitePieceColor);\r
2256   InsertInPalette(blackPieceColor);\r
2257   InsertInPalette(highlightSquareColor);\r
2258   InsertInPalette(premoveHighlightColor);\r
2259 \r
2260   /*  create a logical color palette according the information\r
2261    *  in the LOGPALETTE structure.\r
2262    */\r
2263   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2264 \r
2265   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2266   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2267   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2268   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2269   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2270   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2271   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2272     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2273 \r
2274    /* [AS] Force rendering of the font-based pieces */\r
2275   if( fontBitmapSquareSize > 0 ) {\r
2276     fontBitmapSquareSize = 0;\r
2277   }\r
2278 }\r
2279 \r
2280 \r
2281 int\r
2282 BoardWidth(int boardSize, int n)\r
2283 { /* [HGM] argument n added to allow different width and height */\r
2284   int lineGap = sizeInfo[boardSize].lineGap;\r
2285 \r
2286   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2287       lineGap = appData.overrideLineGap;\r
2288   }\r
2289 \r
2290   return (n + 1) * lineGap +\r
2291           n * sizeInfo[boardSize].squareSize;\r
2292 }\r
2293 \r
2294 /* Respond to board resize by dragging edge */\r
2295 VOID\r
2296 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2297 {\r
2298   BoardSize newSize = NUM_SIZES - 1;\r
2299   static int recurse = 0;\r
2300   if (IsIconic(hwndMain)) return;\r
2301   if (recurse > 0) return;\r
2302   recurse++;\r
2303   while (newSize > 0) {\r
2304         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2305         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2306            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2307     newSize--;\r
2308   } \r
2309   boardSize = newSize;\r
2310   InitDrawingSizes(boardSize, flags);\r
2311   recurse--;\r
2312 }\r
2313 \r
2314 \r
2315 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2316 \r
2317 VOID\r
2318 InitDrawingSizes(BoardSize boardSize, int flags)\r
2319 {\r
2320   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2321   ChessSquare piece;\r
2322   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2323   HDC hdc;\r
2324   SIZE clockSize, messageSize;\r
2325   HFONT oldFont;\r
2326   char buf[MSG_SIZ];\r
2327   char *str;\r
2328   HMENU hmenu = GetMenu(hwndMain);\r
2329   RECT crect, wrect, oldRect;\r
2330   int offby;\r
2331   LOGBRUSH logbrush;\r
2332   VariantClass v = gameInfo.variant;\r
2333 \r
2334   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2335   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2336 \r
2337   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2338   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2339   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2340   oldBoardSize = boardSize;\r
2341 \r
2342   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2343   { // correct board size to one where built-in pieces exist\r
2344     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2345        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2346 \r
2347       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2348       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2349       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2350       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2351       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2352                                    boardSize = SizeMiddling;\r
2353     }\r
2354   }\r
2355   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2356 \r
2357   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2358   oldRect.top = wpMain.y;\r
2359   oldRect.right = wpMain.x + wpMain.width;\r
2360   oldRect.bottom = wpMain.y + wpMain.height;\r
2361 \r
2362   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2363   smallLayout = sizeInfo[boardSize].smallLayout;\r
2364   squareSize = sizeInfo[boardSize].squareSize;\r
2365   lineGap = sizeInfo[boardSize].lineGap;\r
2366   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2367   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2368 \r
2369   // [HGM] decide on tininess based on total board width rather than square size\r
2370   tinyLayout = squareSize * (BOARD_WIDTH);\r
2371   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2372 \r
2373   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2374       lineGap = appData.overrideLineGap;\r
2375   }\r
2376 \r
2377   if (tinyLayout != oldTinyLayout) {\r
2378     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2379     if (tinyLayout == 2) {\r
2380       style &= ~WS_SYSMENU;\r
2381       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2382                  "&Minimize\tCtrl+F4");\r
2383     } else {\r
2384       style |= WS_SYSMENU;\r
2385       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2386     }\r
2387     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2388 \r
2389     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2390       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2391         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2392     }\r
2393     DrawMenuBar(hwndMain);\r
2394   }\r
2395 \r
2396   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2397   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2398 \r
2399   /* Get text area sizes */\r
2400   hdc = GetDC(hwndMain);\r
2401   if (appData.clockMode) {\r
2402     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2403   } else {\r
2404     snprintf(buf, MSG_SIZ, _("White"));\r
2405   }\r
2406   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2407   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2408   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2409   str = _("We only care about the height here");\r
2410   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2411   SelectObject(hdc, oldFont);\r
2412   ReleaseDC(hwndMain, hdc);\r
2413 \r
2414   /* Compute where everything goes */\r
2415   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2416         /* [HGM] logo: if either logo is on, reserve space for it */\r
2417         logoHeight =  2*clockSize.cy;\r
2418         leftLogoRect.left   = OUTER_MARGIN;\r
2419         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2420         leftLogoRect.top    = OUTER_MARGIN;\r
2421         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2422 \r
2423         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2424         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2425         rightLogoRect.top    = OUTER_MARGIN;\r
2426         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2427 \r
2428 \r
2429     whiteRect.left = leftLogoRect.right;\r
2430     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2431     whiteRect.top = OUTER_MARGIN;\r
2432     whiteRect.bottom = whiteRect.top + logoHeight;\r
2433 \r
2434     blackRect.right = rightLogoRect.left;\r
2435     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2436     blackRect.top = whiteRect.top;\r
2437     blackRect.bottom = whiteRect.bottom;\r
2438   } else {\r
2439     whiteRect.left = OUTER_MARGIN;\r
2440     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2441     whiteRect.top = OUTER_MARGIN;\r
2442     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2443 \r
2444     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2445     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2446     blackRect.top = whiteRect.top;\r
2447     blackRect.bottom = whiteRect.bottom;\r
2448 \r
2449     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2450   }\r
2451 \r
2452   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2453   if (appData.showButtonBar) {\r
2454     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2455       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2456   } else {\r
2457     messageRect.right = OUTER_MARGIN + boardWidth;\r
2458   }\r
2459   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2460   messageRect.bottom = messageRect.top + messageSize.cy;\r
2461 \r
2462   boardRect.left = OUTER_MARGIN;\r
2463   boardRect.right = boardRect.left + boardWidth;\r
2464   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2465   boardRect.bottom = boardRect.top + boardHeight;\r
2466 \r
2467   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2468   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2469   oldTinyLayout = tinyLayout;\r
2470   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2471   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2472     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2473   winW *= 1 + twoBoards;\r
2474   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2475   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2476   wpMain.height = winH; //       without disturbing window attachments\r
2477   GetWindowRect(hwndMain, &wrect);\r
2478   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2479                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2480 \r
2481   // [HGM] placement: let attached windows follow size change.\r
2482   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2483   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2484   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2485   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2486   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2487 \r
2488   /* compensate if menu bar wrapped */\r
2489   GetClientRect(hwndMain, &crect);\r
2490   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2491   wpMain.height += offby;\r
2492   switch (flags) {\r
2493   case WMSZ_TOPLEFT:\r
2494     SetWindowPos(hwndMain, NULL, \r
2495                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2496                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2497     break;\r
2498 \r
2499   case WMSZ_TOPRIGHT:\r
2500   case WMSZ_TOP:\r
2501     SetWindowPos(hwndMain, NULL, \r
2502                  wrect.left, wrect.bottom - wpMain.height, \r
2503                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2504     break;\r
2505 \r
2506   case WMSZ_BOTTOMLEFT:\r
2507   case WMSZ_LEFT:\r
2508     SetWindowPos(hwndMain, NULL, \r
2509                  wrect.right - wpMain.width, wrect.top, \r
2510                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2511     break;\r
2512 \r
2513   case WMSZ_BOTTOMRIGHT:\r
2514   case WMSZ_BOTTOM:\r
2515   case WMSZ_RIGHT:\r
2516   default:\r
2517     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2518                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2519     break;\r
2520   }\r
2521 \r
2522   hwndPause = NULL;\r
2523   for (i = 0; i < N_BUTTONS; i++) {\r
2524     if (buttonDesc[i].hwnd != NULL) {\r
2525       DestroyWindow(buttonDesc[i].hwnd);\r
2526       buttonDesc[i].hwnd = NULL;\r
2527     }\r
2528     if (appData.showButtonBar) {\r
2529       buttonDesc[i].hwnd =\r
2530         CreateWindow("BUTTON", buttonDesc[i].label,\r
2531                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2532                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2533                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2534                      (HMENU) buttonDesc[i].id,\r
2535                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2536       if (tinyLayout == 2) {\r
2537         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2538                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2539                     MAKELPARAM(FALSE, 0));\r
2540       }\r
2541       if (buttonDesc[i].id == IDM_Pause)\r
2542         hwndPause = buttonDesc[i].hwnd;\r
2543       buttonDesc[i].wndproc = (WNDPROC)\r
2544         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2545     }\r
2546   }\r
2547   if (gridPen != NULL) DeleteObject(gridPen);\r
2548   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2549   if (premovePen != NULL) DeleteObject(premovePen);\r
2550   if (lineGap != 0) {\r
2551     logbrush.lbStyle = BS_SOLID;\r
2552     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2553     gridPen =\r
2554       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2555                    lineGap, &logbrush, 0, NULL);\r
2556     logbrush.lbColor = highlightSquareColor;\r
2557     highlightPen =\r
2558       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2559                    lineGap, &logbrush, 0, NULL);\r
2560 \r
2561     logbrush.lbColor = premoveHighlightColor; \r
2562     premovePen =\r
2563       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2564                    lineGap, &logbrush, 0, NULL);\r
2565 \r
2566     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2567     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2568       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2569       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2570         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2571       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2572         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2573       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2574     }\r
2575     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2576       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2577       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2578         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2579         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2580       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2581         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2582       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2583     }\r
2584   }\r
2585 \r
2586   /* [HGM] Licensing requirement */\r
2587 #ifdef GOTHIC\r
2588   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2589 #endif\r
2590 #ifdef FALCON\r
2591   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2592 #endif\r
2593   GothicPopUp( "", VariantNormal);\r
2594 \r
2595 \r
2596 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2597 \r
2598   /* Load piece bitmaps for this board size */\r
2599   for (i=0; i<=2; i++) {\r
2600     for (piece = WhitePawn;\r
2601          (int) piece < (int) BlackPawn;\r
2602          piece = (ChessSquare) ((int) piece + 1)) {\r
2603       if (pieceBitmap[i][piece] != NULL)\r
2604         DeleteObject(pieceBitmap[i][piece]);\r
2605       pieceBitmap[i][piece] = NULL;\r
2606     }\r
2607   }\r
2608 \r
2609   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2610 \r
2611   // Orthodox Chess pieces\r
2612   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2613   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2614   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2615   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2616   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2617   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2618   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2619   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2620   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2621   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2622   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2623   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2624   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2625   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2627   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2628     // in Shogi, Hijack the unused Queen for Lance\r
2629     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2630     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2631     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2632   } else {\r
2633     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2634     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2635     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2636   }\r
2637 \r
2638   if(squareSize <= 72 && squareSize >= 33) { \r
2639     /* A & C are available in most sizes now */\r
2640     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2641       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2642       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2643       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2644       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2645       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2646       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2647       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2648       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2649       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2650       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2651       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2652       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2653     } else { // Smirf-like\r
2654       if(gameInfo.variant == VariantSChess) {\r
2655         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2656         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2657         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2658       } else {\r
2659         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2660         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2661         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2662       }\r
2663     }\r
2664     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2665       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2666       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2667       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2668     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2669       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2670       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2671       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2672     } else { // WinBoard standard\r
2673       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2674       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2675       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2676     }\r
2677   }\r
2678 \r
2679 \r
2680   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2681     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2682     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2683     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2684     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2685     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2686     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2687     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2688     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2689     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2690     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2691     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2692     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2693     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2694     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2695     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2696     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2697     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2698     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2699     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2700     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2701     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2702     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2703     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2704     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2705     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2706     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2707     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2708     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2709     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2710     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2711     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2712     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2713     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2714 \r
2715     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2716       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2717       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2718       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2719       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2720       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2721       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2722       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2723       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2724       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2725       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2726       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2727       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2728     } else {\r
2729       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2730       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2731       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2732       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2733       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2734       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2735       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2736       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2737       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2738       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2739       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2740       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2741     }\r
2742 \r
2743   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2744     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2745     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2746     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2747     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2748     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2749     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2750     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2751     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2752     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2753     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2754     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2755     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2756     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2757     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2758   }\r
2759 \r
2760 \r
2761   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2762   /* special Shogi support in this size */\r
2763   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2764       for (piece = WhitePawn;\r
2765            (int) piece < (int) BlackPawn;\r
2766            piece = (ChessSquare) ((int) piece + 1)) {\r
2767         if (pieceBitmap[i][piece] != NULL)\r
2768           DeleteObject(pieceBitmap[i][piece]);\r
2769       }\r
2770     }\r
2771   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2772   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2773   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2774   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2775   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2776   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2777   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2778   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2779   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2780   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2781   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2782   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2783   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2784   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2785   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2786   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2787   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2788   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2789   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2790   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2791   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2792   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2793   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2794   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2795   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2796   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2797   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2798   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2799   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2800   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2801   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2802   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2803   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2804   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2805   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2806   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2807   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2808   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2809   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2810   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2811   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2812   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2813   minorSize = 0;\r
2814   }\r
2815 \r
2816   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2817     char buf[MSG_SIZ];\r
2818     if(pieceBitmap[0][i]) continue;\r
2819     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2820     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2821     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2822     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2823   }\r
2824 }\r
2825 \r
2826 HBITMAP\r
2827 PieceBitmap(ChessSquare p, int kind)\r
2828 {\r
2829   if ((int) p >= (int) BlackPawn)\r
2830     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2831 \r
2832   return pieceBitmap[kind][(int) p];\r
2833 }\r
2834 \r
2835 /***************************************************************/\r
2836 \r
2837 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2838 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2839 /*\r
2840 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2841 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2842 */\r
2843 \r
2844 VOID\r
2845 SquareToPos(int row, int column, int * x, int * y)\r
2846 {\r
2847   if (flipView) {\r
2848     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2849     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2850   } else {\r
2851     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2852     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2853   }\r
2854 }\r
2855 \r
2856 VOID\r
2857 DrawCoordsOnDC(HDC hdc)\r
2858 {\r
2859   static char files[] = "0123456789012345678901221098765432109876543210";\r
2860   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2861   char str[2] = { NULLCHAR, NULLCHAR };\r
2862   int oldMode, oldAlign, x, y, start, i;\r
2863   HFONT oldFont;\r
2864   HBRUSH oldBrush;\r
2865 \r
2866   if (!appData.showCoords)\r
2867     return;\r
2868 \r
2869   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2870 \r
2871   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2872   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2873   oldAlign = GetTextAlign(hdc);\r
2874   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2875 \r
2876   y = boardRect.top + lineGap;\r
2877   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2878 \r
2879   if(border) {\r
2880     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2881     x += border - lineGap - 4; y += squareSize - 6;\r
2882   } else\r
2883   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2884   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2885     str[0] = files[start + i];\r
2886     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2887     y += squareSize + lineGap;\r
2888   }\r
2889 \r
2890   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2891 \r
2892   if(border) {\r
2893     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2894     x += -border + 4; y += border - squareSize + 6;\r
2895   } else\r
2896   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2897   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2898     str[0] = ranks[start + i];\r
2899     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2900     x += squareSize + lineGap;\r
2901   }    \r
2902 \r
2903   SelectObject(hdc, oldBrush);\r
2904   SetBkMode(hdc, oldMode);\r
2905   SetTextAlign(hdc, oldAlign);\r
2906   SelectObject(hdc, oldFont);\r
2907 }\r
2908 \r
2909 VOID\r
2910 DrawGridOnDC(HDC hdc)\r
2911 {\r
2912   HPEN oldPen;\r
2913  \r
2914   if (lineGap != 0) {\r
2915     oldPen = SelectObject(hdc, gridPen);\r
2916     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2917     SelectObject(hdc, oldPen);\r
2918   }\r
2919 }\r
2920 \r
2921 #define HIGHLIGHT_PEN 0\r
2922 #define PREMOVE_PEN   1\r
2923 \r
2924 VOID\r
2925 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2926 {\r
2927   int x1, y1;\r
2928   HPEN oldPen, hPen;\r
2929   if (lineGap == 0) return;\r
2930   if (flipView) {\r
2931     x1 = boardRect.left +\r
2932       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2933     y1 = boardRect.top +\r
2934       lineGap/2 + y * (squareSize + lineGap) + border;\r
2935   } else {\r
2936     x1 = boardRect.left +\r
2937       lineGap/2 + x * (squareSize + lineGap) + border;\r
2938     y1 = boardRect.top +\r
2939       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2940   }\r
2941   hPen = pen ? premovePen : highlightPen;\r
2942   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2943   MoveToEx(hdc, x1, y1, NULL);\r
2944   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2945   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2946   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2947   LineTo(hdc, x1, y1);\r
2948   SelectObject(hdc, oldPen);\r
2949 }\r
2950 \r
2951 VOID\r
2952 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2953 {\r
2954   int i;\r
2955   for (i=0; i<2; i++) {\r
2956     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2957       DrawHighlightOnDC(hdc, TRUE,\r
2958                         h->sq[i].x, h->sq[i].y,\r
2959                         pen);\r
2960   }\r
2961 }\r
2962 \r
2963 /* Note: sqcolor is used only in monoMode */\r
2964 /* Note that this code is largely duplicated in woptions.c,\r
2965    function DrawSampleSquare, so that needs to be updated too */\r
2966 VOID\r
2967 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2968 {\r
2969   HBITMAP oldBitmap;\r
2970   HBRUSH oldBrush;\r
2971   int tmpSize;\r
2972 \r
2973   if (appData.blindfold) return;\r
2974 \r
2975   /* [AS] Use font-based pieces if needed */\r
2976   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2977     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2978     CreatePiecesFromFont();\r
2979 \r
2980     if( fontBitmapSquareSize == squareSize ) {\r
2981         int index = TranslatePieceToFontPiece(piece);\r
2982 \r
2983         SelectObject( tmphdc, hPieceMask[ index ] );\r
2984 \r
2985       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2986         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2987       else\r
2988         BitBlt( hdc,\r
2989             x, y,\r
2990             squareSize, squareSize,\r
2991             tmphdc,\r
2992             0, 0,\r
2993             SRCAND );\r
2994 \r
2995         SelectObject( tmphdc, hPieceFace[ index ] );\r
2996 \r
2997       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2998         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2999       else\r
3000         BitBlt( hdc,\r
3001             x, y,\r
3002             squareSize, squareSize,\r
3003             tmphdc,\r
3004             0, 0,\r
3005             SRCPAINT );\r
3006 \r
3007         return;\r
3008     }\r
3009   }\r
3010 \r
3011   if (appData.monoMode) {\r
3012     SelectObject(tmphdc, PieceBitmap(piece, \r
3013       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3014     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3015            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3016   } else {\r
3017     HBRUSH xBrush = whitePieceBrush;\r
3018     tmpSize = squareSize;\r
3019     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3020     if(minorSize &&\r
3021         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3022          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3023       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3024       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3025       x += (squareSize - minorSize)>>1;\r
3026       y += squareSize - minorSize - 2;\r
3027       tmpSize = minorSize;\r
3028     }\r
3029     if (color || appData.allWhite ) {\r
3030       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3031       if( color )\r
3032               oldBrush = SelectObject(hdc, xBrush);\r
3033       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3034       if(appData.upsideDown && color==flipView)\r
3035         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3036       else\r
3037         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3038       /* Use black for outline of white pieces */\r
3039       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3040       if(appData.upsideDown && color==flipView)\r
3041         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3042       else\r
3043         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3044     } else if(appData.pieceDirectory[0]) {\r
3045       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3046       oldBrush = SelectObject(hdc, xBrush);\r
3047       if(appData.upsideDown && color==flipView)\r
3048         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3049       else\r
3050         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3051       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3052       if(appData.upsideDown && color==flipView)\r
3053         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3054       else\r
3055         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3056     } else {\r
3057       /* Use square color for details of black pieces */\r
3058       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3059       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3060       if(appData.upsideDown && !flipView)\r
3061         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3062       else\r
3063         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3064     }\r
3065     SelectObject(hdc, oldBrush);\r
3066     SelectObject(tmphdc, oldBitmap);\r
3067   }\r
3068 }\r
3069 \r
3070 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3071 int GetBackTextureMode( int algo )\r
3072 {\r
3073     int result = BACK_TEXTURE_MODE_DISABLED;\r
3074 \r
3075     switch( algo ) \r
3076     {\r
3077         case BACK_TEXTURE_MODE_PLAIN:\r
3078             result = 1; /* Always use identity map */\r
3079             break;\r
3080         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3081             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3082             break;\r
3083     }\r
3084 \r
3085     return result;\r
3086 }\r
3087 \r
3088 /* \r
3089     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3090     to handle redraws cleanly (as random numbers would always be different).\r
3091 */\r
3092 VOID RebuildTextureSquareInfo()\r
3093 {\r
3094     BITMAP bi;\r
3095     int lite_w = 0;\r
3096     int lite_h = 0;\r
3097     int dark_w = 0;\r
3098     int dark_h = 0;\r
3099     int row;\r
3100     int col;\r
3101 \r
3102     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3103 \r
3104     if( liteBackTexture != NULL ) {\r
3105         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3106             lite_w = bi.bmWidth;\r
3107             lite_h = bi.bmHeight;\r
3108         }\r
3109     }\r
3110 \r
3111     if( darkBackTexture != NULL ) {\r
3112         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3113             dark_w = bi.bmWidth;\r
3114             dark_h = bi.bmHeight;\r
3115         }\r
3116     }\r
3117 \r
3118     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3119         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3120             if( (col + row) & 1 ) {\r
3121                 /* Lite square */\r
3122                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3123                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3124                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3125                   else\r
3126                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3127                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3128                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3129                   else\r
3130                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3131                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3132                 }\r
3133             }\r
3134             else {\r
3135                 /* Dark square */\r
3136                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3137                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3138                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3139                   else\r
3140                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3141                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3142                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3143                   else\r
3144                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3145                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3146                 }\r
3147             }\r
3148         }\r
3149     }\r
3150 }\r
3151 \r
3152 /* [AS] Arrow highlighting support */\r
3153 \r
3154 static double A_WIDTH = 5; /* Width of arrow body */\r
3155 \r
3156 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3157 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3158 \r
3159 static double Sqr( double x )\r
3160 {\r
3161     return x*x;\r
3162 }\r
3163 \r
3164 static int Round( double x )\r
3165 {\r
3166     return (int) (x + 0.5);\r
3167 }\r
3168 \r
3169 /* Draw an arrow between two points using current settings */\r
3170 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3171 {\r
3172     POINT arrow[7];\r
3173     double dx, dy, j, k, x, y;\r
3174 \r
3175     if( d_x == s_x ) {\r
3176         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3177 \r
3178         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3179         arrow[0].y = s_y;\r
3180 \r
3181         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3182         arrow[1].y = d_y - h;\r
3183 \r
3184         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3185         arrow[2].y = d_y - h;\r
3186 \r
3187         arrow[3].x = d_x;\r
3188         arrow[3].y = d_y;\r
3189 \r
3190         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3191         arrow[5].y = d_y - h;\r
3192 \r
3193         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3194         arrow[4].y = d_y - h;\r
3195 \r
3196         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3197         arrow[6].y = s_y;\r
3198     }\r
3199     else if( d_y == s_y ) {\r
3200         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3201 \r
3202         arrow[0].x = s_x;\r
3203         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3204 \r
3205         arrow[1].x = d_x - w;\r
3206         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3207 \r
3208         arrow[2].x = d_x - w;\r
3209         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3210 \r
3211         arrow[3].x = d_x;\r
3212         arrow[3].y = d_y;\r
3213 \r
3214         arrow[5].x = d_x - w;\r
3215         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3216 \r
3217         arrow[4].x = d_x - w;\r
3218         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3219 \r
3220         arrow[6].x = s_x;\r
3221         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3222     }\r
3223     else {\r
3224         /* [AS] Needed a lot of paper for this! :-) */\r
3225         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3226         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3227   \r
3228         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3229 \r
3230         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3231 \r
3232         x = s_x;\r
3233         y = s_y;\r
3234 \r
3235         arrow[0].x = Round(x - j);\r
3236         arrow[0].y = Round(y + j*dx);\r
3237 \r
3238         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3239         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3240 \r
3241         if( d_x > s_x ) {\r
3242             x = (double) d_x - k;\r
3243             y = (double) d_y - k*dy;\r
3244         }\r
3245         else {\r
3246             x = (double) d_x + k;\r
3247             y = (double) d_y + k*dy;\r
3248         }\r
3249 \r
3250         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3251 \r
3252         arrow[6].x = Round(x - j);\r
3253         arrow[6].y = Round(y + j*dx);\r
3254 \r
3255         arrow[2].x = Round(arrow[6].x + 2*j);\r
3256         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3257 \r
3258         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3259         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3260 \r
3261         arrow[4].x = d_x;\r
3262         arrow[4].y = d_y;\r
3263 \r
3264         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3265         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3266     }\r
3267 \r
3268     Polygon( hdc, arrow, 7 );\r
3269 }\r
3270 \r
3271 /* [AS] Draw an arrow between two squares */\r
3272 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3273 {\r
3274     int s_x, s_y, d_x, d_y;\r
3275     HPEN hpen;\r
3276     HPEN holdpen;\r
3277     HBRUSH hbrush;\r
3278     HBRUSH holdbrush;\r
3279     LOGBRUSH stLB;\r
3280 \r
3281     if( s_col == d_col && s_row == d_row ) {\r
3282         return;\r
3283     }\r
3284 \r
3285     /* Get source and destination points */\r
3286     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3287     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3288 \r
3289     if( d_y > s_y ) {\r
3290         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3291     }\r
3292     else if( d_y < s_y ) {\r
3293         d_y += squareSize / 2 + squareSize / 4;\r
3294     }\r
3295     else {\r
3296         d_y += squareSize / 2;\r
3297     }\r
3298 \r
3299     if( d_x > s_x ) {\r
3300         d_x += squareSize / 2 - squareSize / 4;\r
3301     }\r
3302     else if( d_x < s_x ) {\r
3303         d_x += squareSize / 2 + squareSize / 4;\r
3304     }\r
3305     else {\r
3306         d_x += squareSize / 2;\r
3307     }\r
3308 \r
3309     s_x += squareSize / 2;\r
3310     s_y += squareSize / 2;\r
3311 \r
3312     /* Adjust width */\r
3313     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3314 \r
3315     /* Draw */\r
3316     stLB.lbStyle = BS_SOLID;\r
3317     stLB.lbColor = appData.highlightArrowColor;\r
3318     stLB.lbHatch = 0;\r
3319 \r
3320     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3321     holdpen = SelectObject( hdc, hpen );\r
3322     hbrush = CreateBrushIndirect( &stLB );\r
3323     holdbrush = SelectObject( hdc, hbrush );\r
3324 \r
3325     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3326 \r
3327     SelectObject( hdc, holdpen );\r
3328     SelectObject( hdc, holdbrush );\r
3329     DeleteObject( hpen );\r
3330     DeleteObject( hbrush );\r
3331 }\r
3332 \r
3333 BOOL HasHighlightInfo()\r
3334 {\r
3335     BOOL result = FALSE;\r
3336 \r
3337     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3338         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3339     {\r
3340         result = TRUE;\r
3341     }\r
3342 \r
3343     return result;\r
3344 \r
3345 \r
3346 \r
3347 }\r
3348 \r
3349 BOOL IsDrawArrowEnabled()\r
3350 {\r
3351     BOOL result = FALSE;\r
3352 \r
3353     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3354         result = TRUE;\r
3355     }\r
3356 \r
3357     return result;\r
3358 }\r
3359 \r
3360 VOID DrawArrowHighlight( HDC hdc )\r
3361 {\r
3362     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3363         DrawArrowBetweenSquares( hdc,\r
3364             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3365             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3366     }\r
3367 }\r
3368 \r
3369 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3370 {\r
3371     HRGN result = NULL;\r
3372 \r
3373     if( HasHighlightInfo() ) {\r
3374         int x1, y1, x2, y2;\r
3375         int sx, sy, dx, dy;\r
3376 \r
3377         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3378         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3379 \r
3380         sx = MIN( x1, x2 );\r
3381         sy = MIN( y1, y2 );\r
3382         dx = MAX( x1, x2 ) + squareSize;\r
3383         dy = MAX( y1, y2 ) + squareSize;\r
3384 \r
3385         result = CreateRectRgn( sx, sy, dx, dy );\r
3386     }\r
3387 \r
3388     return result;\r
3389 }\r
3390 \r
3391 /*\r
3392     Warning: this function modifies the behavior of several other functions. \r
3393     \r
3394     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3395     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3396     repaint is scattered all over the place, which is not good for features such as\r
3397     "arrow highlighting" that require a full repaint of the board.\r
3398 \r
3399     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3400     user interaction, when speed is not so important) but especially to avoid errors\r
3401     in the displayed graphics.\r
3402 \r
3403     In such patched places, I always try refer to this function so there is a single\r
3404     place to maintain knowledge.\r
3405     \r
3406     To restore the original behavior, just return FALSE unconditionally.\r
3407 */\r
3408 BOOL IsFullRepaintPreferrable()\r
3409 {\r
3410     BOOL result = FALSE;\r
3411 \r
3412     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3413         /* Arrow may appear on the board */\r
3414         result = TRUE;\r
3415     }\r
3416 \r
3417     return result;\r
3418 }\r
3419 \r
3420 /* \r
3421     This function is called by DrawPosition to know whether a full repaint must\r
3422     be forced or not.\r
3423 \r
3424     Only DrawPosition may directly call this function, which makes use of \r
3425     some state information. Other function should call DrawPosition specifying \r
3426     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3427 */\r
3428 BOOL DrawPositionNeedsFullRepaint()\r
3429 {\r
3430     BOOL result = FALSE;\r
3431 \r
3432     /* \r
3433         Probably a slightly better policy would be to trigger a full repaint\r
3434         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3435         but animation is fast enough that it's difficult to notice.\r
3436     */\r
3437     if( animInfo.piece == EmptySquare ) {\r
3438         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3439             result = TRUE;\r
3440         }\r
3441     }\r
3442 \r
3443     return result;\r
3444 }\r
3445 \r
3446 static HBITMAP borderBitmap;\r
3447 \r
3448 VOID\r
3449 DrawBackgroundOnDC(HDC hdc)\r
3450 {\r
3451   \r
3452   BITMAP bi;\r
3453   HDC tmphdc;\r
3454   HBITMAP hbm;\r
3455   static char oldBorder[MSG_SIZ];\r
3456   int w = 600, h = 600, mode;\r
3457 \r
3458   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3459     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3460     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3461   }\r
3462   if(borderBitmap == NULL) { // loading failed, use white\r
3463     FillRect( hdc, &boardRect, whitePieceBrush );\r
3464     return;\r
3465   }\r
3466   tmphdc = CreateCompatibleDC(hdc);\r
3467   hbm = SelectObject(tmphdc, borderBitmap);\r
3468   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3469             w = bi.bmWidth;\r
3470             h = bi.bmHeight;\r
3471   }\r
3472   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3473   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3474                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3475   SetStretchBltMode(hdc, mode);\r
3476   SelectObject(tmphdc, hbm);\r
3477   DeleteDC(tmphdc);\r
3478 }\r
3479 \r
3480 VOID\r
3481 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3482 {\r
3483   int row, column, x, y, square_color, piece_color;\r
3484   ChessSquare piece;\r
3485   HBRUSH oldBrush;\r
3486   HDC texture_hdc = NULL;\r
3487 \r
3488   /* [AS] Initialize background textures if needed */\r
3489   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3490       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3491       if( backTextureSquareSize != squareSize \r
3492        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3493           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3494           backTextureSquareSize = squareSize;\r
3495           RebuildTextureSquareInfo();\r
3496       }\r
3497 \r
3498       texture_hdc = CreateCompatibleDC( hdc );\r
3499   }\r
3500 \r
3501   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3502     for (column = 0; column < BOARD_WIDTH; column++) {\r
3503   \r
3504       SquareToPos(row, column, &x, &y);\r
3505 \r
3506       piece = board[row][column];\r
3507 \r
3508       square_color = ((column + row) % 2) == 1;\r
3509       if( gameInfo.variant == VariantXiangqi ) {\r
3510           square_color = !InPalace(row, column);\r
3511           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3512           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3513       }\r
3514       piece_color = (int) piece < (int) BlackPawn;\r
3515 \r
3516 \r
3517       /* [HGM] holdings file: light square or black */\r
3518       if(column == BOARD_LEFT-2) {\r
3519             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3520                 square_color = 1;\r
3521             else {\r
3522                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3523                 continue;\r
3524             }\r
3525       } else\r
3526       if(column == BOARD_RGHT + 1 ) {\r
3527             if( row < gameInfo.holdingsSize )\r
3528                 square_color = 1;\r
3529             else {\r
3530                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3531                 continue;\r
3532             }\r
3533       }\r
3534       if(column == BOARD_LEFT-1 ) /* left align */\r
3535             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3536       else if( column == BOARD_RGHT) /* right align */\r
3537             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3538       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3539       else\r
3540       if (appData.monoMode) {\r
3541         if (piece == EmptySquare) {\r
3542           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3543                  square_color ? WHITENESS : BLACKNESS);\r
3544         } else {\r
3545           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3546         }\r
3547       } \r
3548       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3549           /* [AS] Draw the square using a texture bitmap */\r
3550           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3551           int r = row, c = column; // [HGM] do not flip board in flipView\r
3552           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3553 \r
3554           DrawTile( x, y, \r
3555               squareSize, squareSize, \r
3556               hdc, \r
3557               texture_hdc,\r
3558               backTextureSquareInfo[r][c].mode,\r
3559               backTextureSquareInfo[r][c].x,\r
3560               backTextureSquareInfo[r][c].y );\r
3561 \r
3562           SelectObject( texture_hdc, hbm );\r
3563 \r
3564           if (piece != EmptySquare) {\r
3565               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3566           }\r
3567       }\r
3568       else {\r
3569         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3570 \r
3571         oldBrush = SelectObject(hdc, brush );\r
3572         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3573         SelectObject(hdc, oldBrush);\r
3574         if (piece != EmptySquare)\r
3575           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3576       }\r
3577     }\r
3578   }\r
3579 \r
3580   if( texture_hdc != NULL ) {\r
3581     DeleteDC( texture_hdc );\r
3582   }\r
3583 }\r
3584 \r
3585 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3586 void fputDW(FILE *f, int x)\r
3587 {\r
3588         fputc(x     & 255, f);\r
3589         fputc(x>>8  & 255, f);\r
3590         fputc(x>>16 & 255, f);\r
3591         fputc(x>>24 & 255, f);\r
3592 }\r
3593 \r
3594 #define MAX_CLIPS 200   /* more than enough */\r
3595 \r
3596 VOID\r
3597 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3598 {\r
3599 //  HBITMAP bufferBitmap;\r
3600   BITMAP bi;\r
3601 //  RECT Rect;\r
3602   HDC tmphdc;\r
3603   HBITMAP hbm;\r
3604   int w = 100, h = 50;\r
3605 \r
3606   if(logo == NULL) {\r
3607     if(!logoHeight) return;\r
3608     FillRect( hdc, &logoRect, whitePieceBrush );\r
3609   }\r
3610 //  GetClientRect(hwndMain, &Rect);\r
3611 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3612 //                                      Rect.bottom-Rect.top+1);\r
3613   tmphdc = CreateCompatibleDC(hdc);\r
3614   hbm = SelectObject(tmphdc, logo);\r
3615   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3616             w = bi.bmWidth;\r
3617             h = bi.bmHeight;\r
3618   }\r
3619   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3620                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3621   SelectObject(tmphdc, hbm);\r
3622   DeleteDC(tmphdc);\r
3623 }\r
3624 \r
3625 VOID\r
3626 DisplayLogos()\r
3627 {\r
3628   if(logoHeight) {\r
3629         HDC hdc = GetDC(hwndMain);\r
3630         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3631         if(appData.autoLogo) {\r
3632           \r
3633           switch(gameMode) { // pick logos based on game mode\r
3634             case IcsObserving:\r
3635                 whiteLogo = second.programLogo; // ICS logo\r
3636                 blackLogo = second.programLogo;\r
3637             default:\r
3638                 break;\r
3639             case IcsPlayingWhite:\r
3640                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3641                 blackLogo = second.programLogo; // ICS logo\r
3642                 break;\r
3643             case IcsPlayingBlack:\r
3644                 whiteLogo = second.programLogo; // ICS logo\r
3645                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3646                 break;\r
3647             case TwoMachinesPlay:\r
3648                 if(first.twoMachinesColor[0] == 'b') {\r
3649                     whiteLogo = second.programLogo;\r
3650                     blackLogo = first.programLogo;\r
3651                 }\r
3652                 break;\r
3653             case MachinePlaysWhite:\r
3654                 blackLogo = userLogo;\r
3655                 break;\r
3656             case MachinePlaysBlack:\r
3657                 whiteLogo = userLogo;\r
3658                 blackLogo = first.programLogo;\r
3659           }\r
3660         }\r
3661         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3662         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3663         ReleaseDC(hwndMain, hdc);\r
3664   }\r
3665 }\r
3666 \r
3667 void\r
3668 UpdateLogos(int display)\r
3669 { // called after loading new engine(s), in tourney or from menu\r
3670   LoadLogo(&first, 0, FALSE);\r
3671   LoadLogo(&second, 1, appData.icsActive);\r
3672   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3673   if(display) DisplayLogos();\r
3674 }\r
3675 \r
3676 static HDC hdcSeek;\r
3677 \r
3678 // [HGM] seekgraph\r
3679 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3680 {\r
3681     POINT stPt;\r
3682     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3683     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3684     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3685     SelectObject( hdcSeek, hp );\r
3686 }\r
3687 \r
3688 // front-end wrapper for drawing functions to do rectangles\r
3689 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3690 {\r
3691     HPEN hp;\r
3692     RECT rc;\r
3693 \r
3694     if (hdcSeek == NULL) {\r
3695     hdcSeek = GetDC(hwndMain);\r
3696       if (!appData.monoMode) {\r
3697         SelectPalette(hdcSeek, hPal, FALSE);\r
3698         RealizePalette(hdcSeek);\r
3699       }\r
3700     }\r
3701     hp = SelectObject( hdcSeek, gridPen );\r
3702     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3703     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3704     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3705     SelectObject( hdcSeek, hp );\r
3706 }\r
3707 \r
3708 // front-end wrapper for putting text in graph\r
3709 void DrawSeekText(char *buf, int x, int y)\r
3710 {\r
3711         SIZE stSize;\r
3712         SetBkMode( hdcSeek, TRANSPARENT );\r
3713         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3714         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3715 }\r
3716 \r
3717 void DrawSeekDot(int x, int y, int color)\r
3718 {\r
3719         int square = color & 0x80;\r
3720         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3721                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3722         color &= 0x7F;\r
3723         if(square)\r
3724             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3725                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3726         else\r
3727             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3728                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3729             SelectObject(hdcSeek, oldBrush);\r
3730 }\r
3731 \r
3732 void DrawSeekOpen()\r
3733 {\r
3734 }\r
3735 \r
3736 void DrawSeekClose()\r
3737 {\r
3738 }\r
3739 \r
3740 VOID\r
3741 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3742 {\r
3743   static Board lastReq[2], lastDrawn[2];\r
3744   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3745   static int lastDrawnFlipView = 0;\r
3746   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3747   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3748   HDC tmphdc;\r
3749   HDC hdcmem;\r
3750   HBITMAP bufferBitmap;\r
3751   HBITMAP oldBitmap;\r
3752   RECT Rect;\r
3753   HRGN clips[MAX_CLIPS];\r
3754   ChessSquare dragged_piece = EmptySquare;\r
3755   int nr = twoBoards*partnerUp;\r
3756 \r
3757   /* I'm undecided on this - this function figures out whether a full\r
3758    * repaint is necessary on its own, so there's no real reason to have the\r
3759    * caller tell it that.  I think this can safely be set to FALSE - but\r
3760    * if we trust the callers not to request full repaints unnessesarily, then\r
3761    * we could skip some clipping work.  In other words, only request a full\r
3762    * redraw when the majority of pieces have changed positions (ie. flip, \r
3763    * gamestart and similar)  --Hawk\r
3764    */\r
3765   Boolean fullrepaint = repaint;\r
3766 \r
3767   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3768 \r
3769   if( DrawPositionNeedsFullRepaint() ) {\r
3770       fullrepaint = TRUE;\r
3771   }\r
3772 \r
3773   if (board == NULL) {\r
3774     if (!lastReqValid[nr]) {\r
3775       return;\r
3776     }\r
3777     board = lastReq[nr];\r
3778   } else {\r
3779     CopyBoard(lastReq[nr], board);\r
3780     lastReqValid[nr] = 1;\r
3781   }\r
3782 \r
3783   if (doingSizing) {\r
3784     return;\r
3785   }\r
3786 \r
3787   if (IsIconic(hwndMain)) {\r
3788     return;\r
3789   }\r
3790 \r
3791   if (hdc == NULL) {\r
3792     hdc = GetDC(hwndMain);\r
3793     if (!appData.monoMode) {\r
3794       SelectPalette(hdc, hPal, FALSE);\r
3795       RealizePalette(hdc);\r
3796     }\r
3797     releaseDC = TRUE;\r
3798   } else {\r
3799     releaseDC = FALSE;\r
3800   }\r
3801 \r
3802   /* Create some work-DCs */\r
3803   hdcmem = CreateCompatibleDC(hdc);\r
3804   tmphdc = CreateCompatibleDC(hdc);\r
3805 \r
3806   /* If dragging is in progress, we temporarely remove the piece */\r
3807   /* [HGM] or temporarily decrease count if stacked              */\r
3808   /*       !! Moved to before board compare !!                   */\r
3809   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3810     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3811     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3812             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3813         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3814     } else \r
3815     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3816             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3817         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3818     } else \r
3819         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3820   }\r
3821 \r
3822   /* Figure out which squares need updating by comparing the \r
3823    * newest board with the last drawn board and checking if\r
3824    * flipping has changed.\r
3825    */\r
3826   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3827     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3828       for (column = 0; column < BOARD_WIDTH; column++) {\r
3829         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3830           SquareToPos(row, column, &x, &y);\r
3831           clips[num_clips++] =\r
3832             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3833         }\r
3834       }\r
3835     }\r
3836    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3837     for (i=0; i<2; i++) {\r
3838       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3839           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3840         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3841             lastDrawnHighlight.sq[i].y >= 0) {\r
3842           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3843                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3844           clips[num_clips++] =\r
3845             CreateRectRgn(x - lineGap, y - lineGap, \r
3846                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3847         }\r
3848         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3849           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3850           clips[num_clips++] =\r
3851             CreateRectRgn(x - lineGap, y - lineGap, \r
3852                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3853         }\r
3854       }\r
3855     }\r
3856     for (i=0; i<2; i++) {\r
3857       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3858           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3859         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3860             lastDrawnPremove.sq[i].y >= 0) {\r
3861           SquareToPos(lastDrawnPremove.sq[i].y,\r
3862                       lastDrawnPremove.sq[i].x, &x, &y);\r
3863           clips[num_clips++] =\r
3864             CreateRectRgn(x - lineGap, y - lineGap, \r
3865                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3866         }\r
3867         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3868             premoveHighlightInfo.sq[i].y >= 0) {\r
3869           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3870                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3871           clips[num_clips++] =\r
3872             CreateRectRgn(x - lineGap, y - lineGap, \r
3873                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3874         }\r
3875       }\r
3876     }\r
3877    } else { // nr == 1\r
3878         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3879         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3880         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3881         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3882       for (i=0; i<2; i++) {\r
3883         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3884             partnerHighlightInfo.sq[i].y >= 0) {\r
3885           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3886                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3887           clips[num_clips++] =\r
3888             CreateRectRgn(x - lineGap, y - lineGap, \r
3889                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3890         }\r
3891         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3892             oldPartnerHighlight.sq[i].y >= 0) {\r
3893           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3894                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3895           clips[num_clips++] =\r
3896             CreateRectRgn(x - lineGap, y - lineGap, \r
3897                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3898         }\r
3899       }\r
3900    }\r
3901   } else {\r
3902     fullrepaint = TRUE;\r
3903   }\r
3904 \r
3905   /* Create a buffer bitmap - this is the actual bitmap\r
3906    * being written to.  When all the work is done, we can\r
3907    * copy it to the real DC (the screen).  This avoids\r
3908    * the problems with flickering.\r
3909    */\r
3910   GetClientRect(hwndMain, &Rect);\r
3911   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3912                                         Rect.bottom-Rect.top+1);\r
3913   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3914   if (!appData.monoMode) {\r
3915     SelectPalette(hdcmem, hPal, FALSE);\r
3916   }\r
3917 \r
3918   /* Create clips for dragging */\r
3919   if (!fullrepaint) {\r
3920     if (dragInfo.from.x >= 0) {\r
3921       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3922       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3923     }\r
3924     if (dragInfo.start.x >= 0) {\r
3925       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3926       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3927     }\r
3928     if (dragInfo.pos.x >= 0) {\r
3929       x = dragInfo.pos.x - squareSize / 2;\r
3930       y = dragInfo.pos.y - squareSize / 2;\r
3931       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3932     }\r
3933     if (dragInfo.lastpos.x >= 0) {\r
3934       x = dragInfo.lastpos.x - squareSize / 2;\r
3935       y = dragInfo.lastpos.y - squareSize / 2;\r
3936       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3937     }\r
3938   }\r
3939 \r
3940   /* Are we animating a move?  \r
3941    * If so, \r
3942    *   - remove the piece from the board (temporarely)\r
3943    *   - calculate the clipping region\r
3944    */\r
3945   if (!fullrepaint) {\r
3946     if (animInfo.piece != EmptySquare) {\r
3947       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3948       x = boardRect.left + animInfo.lastpos.x;\r
3949       y = boardRect.top + animInfo.lastpos.y;\r
3950       x2 = boardRect.left + animInfo.pos.x;\r
3951       y2 = boardRect.top + animInfo.pos.y;\r
3952       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3953       /* Slight kludge.  The real problem is that after AnimateMove is\r
3954          done, the position on the screen does not match lastDrawn.\r
3955          This currently causes trouble only on e.p. captures in\r
3956          atomic, where the piece moves to an empty square and then\r
3957          explodes.  The old and new positions both had an empty square\r
3958          at the destination, but animation has drawn a piece there and\r
3959          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3960       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3961     }\r
3962   }\r
3963 \r
3964   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3965   if (num_clips == 0)\r
3966     fullrepaint = TRUE;\r
3967 \r
3968   /* Set clipping on the memory DC */\r
3969   if (!fullrepaint) {\r
3970     SelectClipRgn(hdcmem, clips[0]);\r
3971     for (x = 1; x < num_clips; x++) {\r
3972       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3973         abort();  // this should never ever happen!\r
3974     }\r
3975   }\r
3976 \r
3977   /* Do all the drawing to the memory DC */\r
3978   if(explodeInfo.radius) { // [HGM] atomic\r
3979         HBRUSH oldBrush;\r
3980         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3981         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3982         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3983         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3984         x += squareSize/2;\r
3985         y += squareSize/2;\r
3986         if(!fullrepaint) {\r
3987           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3988           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3989         }\r
3990         DrawGridOnDC(hdcmem);\r
3991         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3992         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3993         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3994         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3995         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3996         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3997         SelectObject(hdcmem, oldBrush);\r
3998   } else {\r
3999     if(border) DrawBackgroundOnDC(hdcmem);\r
4000     DrawGridOnDC(hdcmem);\r
4001     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4002         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4003         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4004     } else {\r
4005         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4006         oldPartnerHighlight = partnerHighlightInfo;\r
4007     }\r
4008     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4009   }\r
4010   if(nr == 0) // [HGM] dual: markers only on left board\r
4011   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4012     for (column = 0; column < BOARD_WIDTH; column++) {\r
4013         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4014             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4015             SquareToPos(row, column, &x, &y);\r
4016             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4017                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4018             SelectObject(hdcmem, oldBrush);\r
4019         }\r
4020     }\r
4021   }\r
4022 \r
4023   if( appData.highlightMoveWithArrow ) {\r
4024 \r
4025     DrawArrowHighlight(hdcmem);\r
4026   }\r
4027 \r
4028   DrawCoordsOnDC(hdcmem);\r
4029 \r
4030   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4031                  /* to make sure lastDrawn contains what is actually drawn */\r
4032 \r
4033   /* Put the dragged piece back into place and draw it (out of place!) */\r
4034     if (dragged_piece != EmptySquare) {\r
4035     /* [HGM] or restack */\r
4036     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4037                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4038     else\r
4039     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4040                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4041 \r
4042     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4043     x = dragInfo.pos.x - squareSize / 2;\r
4044     y = dragInfo.pos.y - squareSize / 2;\r
4045     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4046                   ((int) dragInfo.piece < (int) BlackPawn), \r
4047                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4048   }   \r
4049   \r
4050   /* Put the animated piece back into place and draw it */\r
4051   if (animInfo.piece != EmptySquare) {\r
4052     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4053     x = boardRect.left + animInfo.pos.x;\r
4054     y = boardRect.top + animInfo.pos.y;\r
4055     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4056                   ((int) animInfo.piece < (int) BlackPawn),\r
4057                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4058   }\r
4059 \r
4060   /* Release the bufferBitmap by selecting in the old bitmap \r
4061    * and delete the memory DC\r
4062    */\r
4063   SelectObject(hdcmem, oldBitmap);\r
4064   DeleteDC(hdcmem);\r
4065 \r
4066   /* Set clipping on the target DC */\r
4067   if (!fullrepaint) {\r
4068     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4069         RECT rect;\r
4070         GetRgnBox(clips[x], &rect);\r
4071         DeleteObject(clips[x]);\r
4072         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4073                           rect.right + wpMain.width/2, rect.bottom);\r
4074     }\r
4075     SelectClipRgn(hdc, clips[0]);\r
4076     for (x = 1; x < num_clips; x++) {\r
4077       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4078         abort();   // this should never ever happen!\r
4079     } \r
4080   }\r
4081 \r
4082   /* Copy the new bitmap onto the screen in one go.\r
4083    * This way we avoid any flickering\r
4084    */\r
4085   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4086   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4087          boardRect.right - boardRect.left,\r
4088          boardRect.bottom - boardRect.top,\r
4089          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4090   if(saveDiagFlag) { \r
4091     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4092     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4093 \r
4094     GetObject(bufferBitmap, sizeof(b), &b);\r
4095     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4096         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4097         bih.biWidth = b.bmWidth;\r
4098         bih.biHeight = b.bmHeight;\r
4099         bih.biPlanes = 1;\r
4100         bih.biBitCount = b.bmBitsPixel;\r
4101         bih.biCompression = 0;\r
4102         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4103         bih.biXPelsPerMeter = 0;\r
4104         bih.biYPelsPerMeter = 0;\r
4105         bih.biClrUsed = 0;\r
4106         bih.biClrImportant = 0;\r
4107 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4108 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4109         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4110 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4111 \r
4112         wb = b.bmWidthBytes;\r
4113         // count colors\r
4114         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4115                 int k = ((int*) pData)[i];\r
4116                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4117                 if(j >= 16) break;\r
4118                 color[j] = k;\r
4119                 if(j >= nrColors) nrColors = j+1;\r
4120         }\r
4121         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4122                 INT p = 0;\r
4123                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4124                     for(w=0; w<(wb>>2); w+=2) {\r
4125                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4126                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4127                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4128                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4129                         pData[p++] = m | j<<4;\r
4130                     }\r
4131                     while(p&3) pData[p++] = 0;\r
4132                 }\r
4133                 fac = 3;\r
4134                 wb = ((wb+31)>>5)<<2;\r
4135         }\r
4136         // write BITMAPFILEHEADER\r
4137         fprintf(diagFile, "BM");\r
4138         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4139         fputDW(diagFile, 0);\r
4140         fputDW(diagFile, 0x36 + (fac?64:0));\r
4141         // write BITMAPINFOHEADER\r
4142         fputDW(diagFile, 40);\r
4143         fputDW(diagFile, b.bmWidth);\r
4144         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4145         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4146         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4147         fputDW(diagFile, 0);\r
4148         fputDW(diagFile, 0);\r
4149         fputDW(diagFile, 0);\r
4150         fputDW(diagFile, 0);\r
4151         fputDW(diagFile, 0);\r
4152         fputDW(diagFile, 0);\r
4153         // write color table\r
4154         if(fac)\r
4155         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4156         // write bitmap data\r
4157         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4158                 fputc(pData[i], diagFile);\r
4159         free(pData);\r
4160      }\r
4161   }\r
4162 \r
4163   SelectObject(tmphdc, oldBitmap);\r
4164 \r
4165   /* Massive cleanup */\r
4166   for (x = 0; x < num_clips; x++)\r
4167     DeleteObject(clips[x]);\r
4168 \r
4169   DeleteDC(tmphdc);\r
4170   DeleteObject(bufferBitmap);\r
4171 \r
4172   if (releaseDC) \r
4173     ReleaseDC(hwndMain, hdc);\r
4174   \r
4175   if (lastDrawnFlipView != flipView && nr == 0) {\r
4176     if (flipView)\r
4177       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4178     else\r
4179       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4180   }\r
4181 \r
4182 /*  CopyBoard(lastDrawn, board);*/\r
4183   lastDrawnHighlight = highlightInfo;\r
4184   lastDrawnPremove   = premoveHighlightInfo;\r
4185   lastDrawnFlipView = flipView;\r
4186   lastDrawnValid[nr] = 1;\r
4187 }\r
4188 \r
4189 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4190 int\r
4191 SaveDiagram(f)\r
4192      FILE *f;\r
4193 {\r
4194     saveDiagFlag = 1; diagFile = f;\r
4195     HDCDrawPosition(NULL, TRUE, NULL);\r
4196     saveDiagFlag = 0;\r
4197 \r
4198     fclose(f);\r
4199     return TRUE;\r
4200 }\r
4201 \r
4202 \r
4203 /*---------------------------------------------------------------------------*\\r
4204 | CLIENT PAINT PROCEDURE\r
4205 |   This is the main event-handler for the WM_PAINT message.\r
4206 |\r
4207 \*---------------------------------------------------------------------------*/\r
4208 VOID\r
4209 PaintProc(HWND hwnd)\r
4210 {\r
4211   HDC         hdc;\r
4212   PAINTSTRUCT ps;\r
4213   HFONT       oldFont;\r
4214 \r
4215   if((hdc = BeginPaint(hwnd, &ps))) {\r
4216     if (IsIconic(hwnd)) {\r
4217       DrawIcon(hdc, 2, 2, iconCurrent);\r
4218     } else {\r
4219       if (!appData.monoMode) {\r
4220         SelectPalette(hdc, hPal, FALSE);\r
4221         RealizePalette(hdc);\r
4222       }\r
4223       HDCDrawPosition(hdc, 1, NULL);\r
4224       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4225         flipView = !flipView; partnerUp = !partnerUp;\r
4226         HDCDrawPosition(hdc, 1, NULL);\r
4227         flipView = !flipView; partnerUp = !partnerUp;\r
4228       }\r
4229       oldFont =\r
4230         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4231       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4232                  ETO_CLIPPED|ETO_OPAQUE,\r
4233                  &messageRect, messageText, strlen(messageText), NULL);\r
4234       SelectObject(hdc, oldFont);\r
4235       DisplayBothClocks();\r
4236       DisplayLogos();\r
4237     }\r
4238     EndPaint(hwnd,&ps);\r
4239   }\r
4240 \r
4241   return;\r
4242 }\r
4243 \r
4244 \r
4245 /*\r
4246  * If the user selects on a border boundary, return -1; if off the board,\r
4247  *   return -2.  Otherwise map the event coordinate to the square.\r
4248  * The offset boardRect.left or boardRect.top must already have been\r
4249  *   subtracted from x.\r
4250  */\r
4251 int EventToSquare(x, limit)\r
4252      int x, limit;\r
4253 {\r
4254   if (x <= border)\r
4255     return -2;\r
4256   if (x < lineGap + border)\r
4257     return -1;\r
4258   x -= lineGap + border;\r
4259   if ((x % (squareSize + lineGap)) >= squareSize)\r
4260     return -1;\r
4261   x /= (squareSize + lineGap);\r
4262     if (x >= limit)\r
4263     return -2;\r
4264   return x;\r
4265 }\r
4266 \r
4267 typedef struct {\r
4268   char piece;\r
4269   int command;\r
4270   char* name;\r
4271 } DropEnable;\r
4272 \r
4273 DropEnable dropEnables[] = {\r
4274   { 'P', DP_Pawn, N_("Pawn") },\r
4275   { 'N', DP_Knight, N_("Knight") },\r
4276   { 'B', DP_Bishop, N_("Bishop") },\r
4277   { 'R', DP_Rook, N_("Rook") },\r
4278   { 'Q', DP_Queen, N_("Queen") },\r
4279 };\r
4280 \r
4281 VOID\r
4282 SetupDropMenu(HMENU hmenu)\r
4283 {\r
4284   int i, count, enable;\r
4285   char *p;\r
4286   extern char white_holding[], black_holding[];\r
4287   char item[MSG_SIZ];\r
4288 \r
4289   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4290     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4291                dropEnables[i].piece);\r
4292     count = 0;\r
4293     while (p && *p++ == dropEnables[i].piece) count++;\r
4294       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4295     enable = count > 0 || !appData.testLegality\r
4296       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4297                       && !appData.icsActive);\r
4298     ModifyMenu(hmenu, dropEnables[i].command,\r
4299                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4300                dropEnables[i].command, item);\r
4301   }\r
4302 }\r
4303 \r
4304 void DragPieceBegin(int x, int y, Boolean instantly)\r
4305 {\r
4306       dragInfo.lastpos.x = boardRect.left + x;\r
4307       dragInfo.lastpos.y = boardRect.top + y;\r
4308       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4309       dragInfo.from.x = fromX;\r
4310       dragInfo.from.y = fromY;\r
4311       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4312       dragInfo.start = dragInfo.from;\r
4313       SetCapture(hwndMain);\r
4314 }\r
4315 \r
4316 void DragPieceEnd(int x, int y)\r
4317 {\r
4318     ReleaseCapture();\r
4319     dragInfo.start.x = dragInfo.start.y = -1;\r
4320     dragInfo.from = dragInfo.start;\r
4321     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4322 }\r
4323 \r
4324 void ChangeDragPiece(ChessSquare piece)\r
4325 {\r
4326     dragInfo.piece = piece;\r
4327 }\r
4328 \r
4329 /* Event handler for mouse messages */\r
4330 VOID\r
4331 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4332 {\r
4333   int x, y, menuNr;\r
4334   POINT pt;\r
4335   static int recursive = 0;\r
4336   HMENU hmenu;\r
4337   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4338 \r
4339   if (recursive) {\r
4340     if (message == WM_MBUTTONUP) {\r
4341       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4342          to the middle button: we simulate pressing the left button too!\r
4343          */\r
4344       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4345       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4346     }\r
4347     return;\r
4348   }\r
4349   recursive++;\r
4350   \r
4351   pt.x = LOWORD(lParam);\r
4352   pt.y = HIWORD(lParam);\r
4353   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4354   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4355   if (!flipView && y >= 0) {\r
4356     y = BOARD_HEIGHT - 1 - y;\r
4357   }\r
4358   if (flipView && x >= 0) {\r
4359     x = BOARD_WIDTH - 1 - x;\r
4360   }\r
4361 \r
4362   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4363   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4364 \r
4365   switch (message) {\r
4366   case WM_LBUTTONDOWN:\r
4367       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4368         ClockClick(flipClock); break;\r
4369       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4370         ClockClick(!flipClock); break;\r
4371       }\r
4372     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4373       dragInfo.start.x = dragInfo.start.y = -1;\r
4374       dragInfo.from = dragInfo.start;\r
4375     }\r
4376     if(fromX == -1 && frozen) { // not sure where this is for\r
4377                 fromX = fromY = -1; \r
4378       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4379       break;\r
4380     }\r
4381       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4382       DrawPosition(TRUE, NULL);\r
4383     break;\r
4384 \r
4385   case WM_LBUTTONUP:\r
4386       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4387       DrawPosition(TRUE, NULL);\r
4388     break;\r
4389 \r
4390   case WM_MOUSEMOVE:\r
4391     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4392     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4393     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4394     if ((appData.animateDragging || appData.highlightDragging)\r
4395         && (wParam & MK_LBUTTON || dragging == 2)\r
4396         && dragInfo.from.x >= 0) \r
4397     {\r
4398       BOOL full_repaint = FALSE;\r
4399 \r
4400       if (appData.animateDragging) {\r
4401         dragInfo.pos = pt;\r
4402       }\r
4403       if (appData.highlightDragging) {\r
4404         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4405         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4406             full_repaint = TRUE;\r
4407         }\r
4408       }\r
4409       \r
4410       DrawPosition( full_repaint, NULL);\r
4411       \r
4412       dragInfo.lastpos = dragInfo.pos;\r
4413     }\r
4414     break;\r
4415 \r
4416   case WM_MOUSEWHEEL: // [DM]\r
4417     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4418        /* Mouse Wheel is being rolled forward\r
4419         * Play moves forward\r
4420         */\r
4421        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4422                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4423        /* Mouse Wheel is being rolled backward\r
4424         * Play moves backward\r
4425         */\r
4426        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4427                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4428     }\r
4429     break;\r
4430 \r
4431   case WM_MBUTTONUP:\r
4432   case WM_RBUTTONUP:\r
4433     ReleaseCapture();\r
4434     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4435     break;\r
4436  \r
4437   case WM_MBUTTONDOWN:\r
4438   case WM_RBUTTONDOWN:\r
4439     ErrorPopDown();\r
4440     ReleaseCapture();\r
4441     fromX = fromY = -1;\r
4442     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4443     dragInfo.start.x = dragInfo.start.y = -1;\r
4444     dragInfo.from = dragInfo.start;\r
4445     dragInfo.lastpos = dragInfo.pos;\r
4446     if (appData.highlightDragging) {\r
4447       ClearHighlights();\r
4448     }\r
4449     if(y == -2) {\r
4450       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4451       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4452           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4453       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4454           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4455       }\r
4456       break;\r
4457     }\r
4458     DrawPosition(TRUE, NULL);\r
4459 \r
4460     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4461     switch (menuNr) {\r
4462     case 0:\r
4463       if (message == WM_MBUTTONDOWN) {\r
4464         buttonCount = 3;  /* even if system didn't think so */\r
4465         if (wParam & MK_SHIFT) \r
4466           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4467         else\r
4468           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4469       } else { /* message == WM_RBUTTONDOWN */\r
4470         /* Just have one menu, on the right button.  Windows users don't\r
4471            think to try the middle one, and sometimes other software steals\r
4472            it, or it doesn't really exist. */\r
4473         if(gameInfo.variant != VariantShogi)\r
4474             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4475         else\r
4476             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4477       }\r
4478       break;\r
4479     case 2:\r
4480       SetCapture(hwndMain);\r
4481       break;\r
4482     case 1:\r
4483       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4484       SetupDropMenu(hmenu);\r
4485       MenuPopup(hwnd, pt, hmenu, -1);\r
4486     default:\r
4487       break;\r
4488     }\r
4489     break;\r
4490   }\r
4491 \r
4492   recursive--;\r
4493 }\r
4494 \r
4495 /* Preprocess messages for buttons in main window */\r
4496 LRESULT CALLBACK\r
4497 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4498 {\r
4499   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4500   int i, dir;\r
4501 \r
4502   for (i=0; i<N_BUTTONS; i++) {\r
4503     if (buttonDesc[i].id == id) break;\r
4504   }\r
4505   if (i == N_BUTTONS) return 0;\r
4506   switch (message) {\r
4507   case WM_KEYDOWN:\r
4508     switch (wParam) {\r
4509     case VK_LEFT:\r
4510     case VK_RIGHT:\r
4511       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4512       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4513       return TRUE;\r
4514     }\r
4515     break;\r
4516   case WM_CHAR:\r
4517     switch (wParam) {\r
4518     case '\r':\r
4519       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4520       return TRUE;\r
4521     default:\r
4522       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4523         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4524         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4525         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4526         SetFocus(h);\r
4527         SendMessage(h, WM_CHAR, wParam, lParam);\r
4528         return TRUE;\r
4529       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4530         TypeInEvent((char)wParam);\r
4531       }\r
4532       break;\r
4533     }\r
4534     break;\r
4535   }\r
4536   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4537 }\r
4538 \r
4539 static int promoStyle;\r
4540 \r
4541 /* Process messages for Promotion dialog box */\r
4542 LRESULT CALLBACK\r
4543 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4544 {\r
4545 \r
4546   char promoChar;\r
4547 \r
4548   switch (message) {\r
4549 \r
4550   case WM_INITDIALOG: /* message: initialize dialog box */\r
4551     /* Center the dialog over the application window */\r
4552     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4553     Translate(hDlg, DLG_PromotionKing);\r
4554     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4555       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4556        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4557        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4558                SW_SHOW : SW_HIDE);\r
4559     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4560     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4561        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4562          PieceToChar(WhiteAngel) != '~') ||\r
4563         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4564          PieceToChar(BlackAngel) != '~')   ) ?\r
4565                SW_SHOW : SW_HIDE);\r
4566     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4567        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4568          PieceToChar(WhiteMarshall) != '~') ||\r
4569         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4570          PieceToChar(BlackMarshall) != '~')   ) ?\r
4571                SW_SHOW : SW_HIDE);\r
4572     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4573     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4574     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4575     if(promoStyle) {\r
4576         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4577         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4578         SetWindowText(hDlg, "Promote?");\r
4579     }\r
4580     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4581        gameInfo.variant == VariantSuper ?\r
4582                SW_SHOW : SW_HIDE);\r
4583     return TRUE;\r
4584 \r
4585   case WM_COMMAND: /* message: received a command */\r
4586     switch (LOWORD(wParam)) {\r
4587     case IDCANCEL:\r
4588       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4589       ClearHighlights();\r
4590       DrawPosition(FALSE, NULL);\r
4591       return TRUE;\r
4592     case PB_King:\r
4593       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4594       break;\r
4595     case PB_Queen:\r
4596       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4597       break;\r
4598     case PB_Rook:\r
4599       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4600       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4601       break;\r
4602     case PB_Bishop:\r
4603       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4604       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4605       break;\r
4606     case PB_Chancellor:\r
4607       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4608       break;\r
4609     case PB_Archbishop:\r
4610       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4611       break;\r
4612     case PB_Knight:\r
4613       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4614                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4615       break;\r
4616     default:\r
4617       return FALSE;\r
4618     }\r
4619     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4620     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4621     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4622     fromX = fromY = -1;\r
4623     if (!appData.highlightLastMove) {\r
4624       ClearHighlights();\r
4625       DrawPosition(FALSE, NULL);\r
4626     }\r
4627     return TRUE;\r
4628   }\r
4629   return FALSE;\r
4630 }\r
4631 \r
4632 /* Pop up promotion dialog */\r
4633 VOID\r
4634 PromotionPopup(HWND hwnd)\r
4635 {\r
4636   FARPROC lpProc;\r
4637 \r
4638   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4639   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4640     hwnd, (DLGPROC)lpProc);\r
4641   FreeProcInstance(lpProc);\r
4642 }\r
4643 \r
4644 void\r
4645 PromotionPopUp(char choice)\r
4646 {\r
4647   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4648   DrawPosition(TRUE, NULL);\r
4649   PromotionPopup(hwndMain);\r
4650 }\r
4651 \r
4652 VOID\r
4653 LoadGameDialog(HWND hwnd, char* title)\r
4654 {\r
4655   UINT number = 0;\r
4656   FILE *f;\r
4657   char fileTitle[MSG_SIZ];\r
4658   f = OpenFileDialog(hwnd, "rb", "",\r
4659                      appData.oldSaveStyle ? "gam" : "pgn",\r
4660                      GAME_FILT,\r
4661                      title, &number, fileTitle, NULL);\r
4662   if (f != NULL) {\r
4663     cmailMsgLoaded = FALSE;\r
4664     if (number == 0) {\r
4665       int error = GameListBuild(f);\r
4666       if (error) {\r
4667         DisplayError(_("Cannot build game list"), error);\r
4668       } else if (!ListEmpty(&gameList) &&\r
4669                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4670         GameListPopUp(f, fileTitle);\r
4671         return;\r
4672       }\r
4673       GameListDestroy();\r
4674       number = 1;\r
4675     }\r
4676     LoadGame(f, number, fileTitle, FALSE);\r
4677   }\r
4678 }\r
4679 \r
4680 int get_term_width()\r
4681 {\r
4682     HDC hdc;\r
4683     TEXTMETRIC tm;\r
4684     RECT rc;\r
4685     HFONT hfont, hold_font;\r
4686     LOGFONT lf;\r
4687     HWND hText;\r
4688 \r
4689     if (hwndConsole)\r
4690         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4691     else\r
4692         return 79;\r
4693 \r
4694     // get the text metrics\r
4695     hdc = GetDC(hText);\r
4696     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4697     if (consoleCF.dwEffects & CFE_BOLD)\r
4698         lf.lfWeight = FW_BOLD;\r
4699     if (consoleCF.dwEffects & CFE_ITALIC)\r
4700         lf.lfItalic = TRUE;\r
4701     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4702         lf.lfStrikeOut = TRUE;\r
4703     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4704         lf.lfUnderline = TRUE;\r
4705     hfont = CreateFontIndirect(&lf);\r
4706     hold_font = SelectObject(hdc, hfont);\r
4707     GetTextMetrics(hdc, &tm);\r
4708     SelectObject(hdc, hold_font);\r
4709     DeleteObject(hfont);\r
4710     ReleaseDC(hText, hdc);\r
4711 \r
4712     // get the rectangle\r
4713     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4714 \r
4715     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4716 }\r
4717 \r
4718 void UpdateICSWidth(HWND hText)\r
4719 {\r
4720     LONG old_width, new_width;\r
4721 \r
4722     new_width = get_term_width(hText, FALSE);\r
4723     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4724     if (new_width != old_width)\r
4725     {\r
4726         ics_update_width(new_width);\r
4727         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4728     }\r
4729 }\r
4730 \r
4731 VOID\r
4732 ChangedConsoleFont()\r
4733 {\r
4734   CHARFORMAT cfmt;\r
4735   CHARRANGE tmpsel, sel;\r
4736   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4737   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4738   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4739   PARAFORMAT paraf;\r
4740 \r
4741   cfmt.cbSize = sizeof(CHARFORMAT);\r
4742   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4743     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4744                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4745   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4746    * size.  This was undocumented in the version of MSVC++ that I had\r
4747    * when I wrote the code, but is apparently documented now.\r
4748    */\r
4749   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4750   cfmt.bCharSet = f->lf.lfCharSet;\r
4751   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4752   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4753   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4754   /* Why are the following seemingly needed too? */\r
4755   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4756   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4757   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4758   tmpsel.cpMin = 0;\r
4759   tmpsel.cpMax = -1; /*999999?*/\r
4760   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4761   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4762   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4763    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4764    */\r
4765   paraf.cbSize = sizeof(paraf);\r
4766   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4767   paraf.dxStartIndent = 0;\r
4768   paraf.dxOffset = WRAP_INDENT;\r
4769   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4770   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4771   UpdateICSWidth(hText);\r
4772 }\r
4773 \r
4774 /*---------------------------------------------------------------------------*\\r
4775  *\r
4776  * Window Proc for main window\r
4777  *\r
4778 \*---------------------------------------------------------------------------*/\r
4779 \r
4780 /* Process messages for main window, etc. */\r
4781 LRESULT CALLBACK\r
4782 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4783 {\r
4784   FARPROC lpProc;\r
4785   int wmId;\r
4786   char *defName;\r
4787   FILE *f;\r
4788   UINT number;\r
4789   char fileTitle[MSG_SIZ];\r
4790   static SnapData sd;\r
4791   static int peek=0;\r
4792 \r
4793   switch (message) {\r
4794 \r
4795   case WM_PAINT: /* message: repaint portion of window */\r
4796     PaintProc(hwnd);\r
4797     break;\r
4798 \r
4799   case WM_ERASEBKGND:\r
4800     if (IsIconic(hwnd)) {\r
4801       /* Cheat; change the message */\r
4802       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4803     } else {\r
4804       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4805     }\r
4806     break;\r
4807 \r
4808   case WM_LBUTTONDOWN:\r
4809   case WM_MBUTTONDOWN:\r
4810   case WM_RBUTTONDOWN:\r
4811   case WM_LBUTTONUP:\r
4812   case WM_MBUTTONUP:\r
4813   case WM_RBUTTONUP:\r
4814   case WM_MOUSEMOVE:\r
4815   case WM_MOUSEWHEEL:\r
4816     MouseEvent(hwnd, message, wParam, lParam);\r
4817     break;\r
4818 \r
4819   case WM_KEYUP:\r
4820     if((char)wParam == '\b') {\r
4821       ForwardEvent(); peek = 0;\r
4822     }\r
4823 \r
4824     JAWS_KBUP_NAVIGATION\r
4825 \r
4826     break;\r
4827 \r
4828   case WM_KEYDOWN:\r
4829     if((char)wParam == '\b') {\r
4830       if(!peek) BackwardEvent(), peek = 1;\r
4831     }\r
4832 \r
4833     JAWS_KBDOWN_NAVIGATION\r
4834 \r
4835     break;\r
4836 \r
4837   case WM_CHAR:\r
4838     \r
4839     JAWS_ALT_INTERCEPT\r
4840 \r
4841     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4842         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4843         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4844         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4845         SetFocus(h);\r
4846         SendMessage(h, message, wParam, lParam);\r
4847     } else if(lParam != KF_REPEAT) {\r
4848         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4849                 TypeInEvent((char)wParam);\r
4850         } else if((char)wParam == 003) CopyGameToClipboard();\r
4851          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4852     }\r
4853 \r
4854     break;\r
4855 \r
4856   case WM_PALETTECHANGED:\r
4857     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4858       int nnew;\r
4859       HDC hdc = GetDC(hwndMain);\r
4860       SelectPalette(hdc, hPal, TRUE);\r
4861       nnew = RealizePalette(hdc);\r
4862       if (nnew > 0) {\r
4863         paletteChanged = TRUE;\r
4864 \r
4865         InvalidateRect(hwnd, &boardRect, FALSE);\r
4866       }\r
4867       ReleaseDC(hwnd, hdc);\r
4868     }\r
4869     break;\r
4870 \r
4871   case WM_QUERYNEWPALETTE:\r
4872     if (!appData.monoMode /*&& paletteChanged*/) {\r
4873       int nnew;\r
4874       HDC hdc = GetDC(hwndMain);\r
4875       paletteChanged = FALSE;\r
4876       SelectPalette(hdc, hPal, FALSE);\r
4877       nnew = RealizePalette(hdc);\r
4878       if (nnew > 0) {\r
4879         InvalidateRect(hwnd, &boardRect, FALSE);\r
4880       }\r
4881       ReleaseDC(hwnd, hdc);\r
4882       return TRUE;\r
4883     }\r
4884     return FALSE;\r
4885 \r
4886   case WM_COMMAND: /* message: command from application menu */\r
4887     wmId    = LOWORD(wParam);\r
4888 \r
4889     switch (wmId) {\r
4890     case IDM_NewGame:\r
4891       ResetGameEvent();\r
4892       SAY("new game enter a move to play against the computer with white");\r
4893       break;\r
4894 \r
4895     case IDM_NewGameFRC:\r
4896       if( NewGameFRC() == 0 ) {\r
4897         ResetGameEvent();\r
4898       }\r
4899       break;\r
4900 \r
4901     case IDM_NewVariant:\r
4902       NewVariantPopup(hwnd);\r
4903       break;\r
4904 \r
4905     case IDM_LoadGame:\r
4906       LoadGameDialog(hwnd, _("Load Game from File"));\r
4907       break;\r
4908 \r
4909     case IDM_LoadNextGame:\r
4910       ReloadGame(1);\r
4911       break;\r
4912 \r
4913     case IDM_LoadPrevGame:\r
4914       ReloadGame(-1);\r
4915       break;\r
4916 \r
4917     case IDM_ReloadGame:\r
4918       ReloadGame(0);\r
4919       break;\r
4920 \r
4921     case IDM_LoadPosition:\r
4922       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4923         Reset(FALSE, TRUE);\r
4924       }\r
4925       number = 1;\r
4926       f = OpenFileDialog(hwnd, "rb", "",\r
4927                          appData.oldSaveStyle ? "pos" : "fen",\r
4928                          POSITION_FILT,\r
4929                          _("Load Position from File"), &number, fileTitle, NULL);\r
4930       if (f != NULL) {\r
4931         LoadPosition(f, number, fileTitle);\r
4932       }\r
4933       break;\r
4934 \r
4935     case IDM_LoadNextPosition:\r
4936       ReloadPosition(1);\r
4937       break;\r
4938 \r
4939     case IDM_LoadPrevPosition:\r
4940       ReloadPosition(-1);\r
4941       break;\r
4942 \r
4943     case IDM_ReloadPosition:\r
4944       ReloadPosition(0);\r
4945       break;\r
4946 \r
4947     case IDM_SaveGame:\r
4948       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4949       f = OpenFileDialog(hwnd, "a", defName,\r
4950                          appData.oldSaveStyle ? "gam" : "pgn",\r
4951                          GAME_FILT,\r
4952                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4953       if (f != NULL) {\r
4954         SaveGame(f, 0, "");\r
4955       }\r
4956       break;\r
4957 \r
4958     case IDM_SavePosition:\r
4959       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4960       f = OpenFileDialog(hwnd, "a", defName,\r
4961                          appData.oldSaveStyle ? "pos" : "fen",\r
4962                          POSITION_FILT,\r
4963                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4964       if (f != NULL) {\r
4965         SavePosition(f, 0, "");\r
4966       }\r
4967       break;\r
4968 \r
4969     case IDM_SaveDiagram:\r
4970       defName = "diagram";\r
4971       f = OpenFileDialog(hwnd, "wb", defName,\r
4972                          "bmp",\r
4973                          DIAGRAM_FILT,\r
4974                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4975       if (f != NULL) {\r
4976         SaveDiagram(f);\r
4977       }\r
4978       break;\r
4979 \r
4980     case IDM_SaveSelected:\r
4981       f = OpenFileDialog(hwnd, "a", "",\r
4982                          "pgn",\r
4983                          GAME_FILT,\r
4984                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4985       if (f != NULL) {\r
4986         SaveSelected(f, 0, "");\r
4987       }\r
4988       break;\r
4989 \r
4990     case IDM_CreateBook:\r
4991       CreateBookEvent();\r
4992       break;\r
4993 \r
4994     case IDM_CopyGame:\r
4995       CopyGameToClipboard();\r
4996       break;\r
4997 \r
4998     case IDM_PasteGame:\r
4999       PasteGameFromClipboard();\r
5000       break;\r
5001 \r
5002     case IDM_CopyGameListToClipboard:\r
5003       CopyGameListToClipboard();\r
5004       break;\r
5005 \r
5006     /* [AS] Autodetect FEN or PGN data */\r
5007     case IDM_PasteAny:\r
5008       PasteGameOrFENFromClipboard();\r
5009       break;\r
5010 \r
5011     /* [AS] Move history */\r
5012     case IDM_ShowMoveHistory:\r
5013         if( MoveHistoryIsUp() ) {\r
5014             MoveHistoryPopDown();\r
5015         }\r
5016         else {\r
5017             MoveHistoryPopUp();\r
5018         }\r
5019         break;\r
5020 \r
5021     /* [AS] Eval graph */\r
5022     case IDM_ShowEvalGraph:\r
5023         if( EvalGraphIsUp() ) {\r
5024             EvalGraphPopDown();\r
5025         }\r
5026         else {\r
5027             EvalGraphPopUp();\r
5028             SetFocus(hwndMain);\r
5029         }\r
5030         break;\r
5031 \r
5032     /* [AS] Engine output */\r
5033     case IDM_ShowEngineOutput:\r
5034         if( EngineOutputIsUp() ) {\r
5035             EngineOutputPopDown();\r
5036         }\r
5037         else {\r
5038             EngineOutputPopUp();\r
5039         }\r
5040         break;\r
5041 \r
5042     /* [AS] User adjudication */\r
5043     case IDM_UserAdjudication_White:\r
5044         UserAdjudicationEvent( +1 );\r
5045         break;\r
5046 \r
5047     case IDM_UserAdjudication_Black:\r
5048         UserAdjudicationEvent( -1 );\r
5049         break;\r
5050 \r
5051     case IDM_UserAdjudication_Draw:\r
5052         UserAdjudicationEvent( 0 );\r
5053         break;\r
5054 \r
5055     /* [AS] Game list options dialog */\r
5056     case IDM_GameListOptions:\r
5057       GameListOptions();\r
5058       break;\r
5059 \r
5060     case IDM_NewChat:\r
5061       ChatPopUp(NULL);\r
5062       break;\r
5063 \r
5064     case IDM_CopyPosition:\r
5065       CopyFENToClipboard();\r
5066       break;\r
5067 \r
5068     case IDM_PastePosition:\r
5069       PasteFENFromClipboard();\r
5070       break;\r
5071 \r
5072     case IDM_MailMove:\r
5073       MailMoveEvent();\r
5074       break;\r
5075 \r
5076     case IDM_ReloadCMailMsg:\r
5077       Reset(TRUE, TRUE);\r
5078       ReloadCmailMsgEvent(FALSE);\r
5079       break;\r
5080 \r
5081     case IDM_Minimize:\r
5082       ShowWindow(hwnd, SW_MINIMIZE);\r
5083       break;\r
5084 \r
5085     case IDM_Exit:\r
5086       ExitEvent(0);\r
5087       break;\r
5088 \r
5089     case IDM_MachineWhite:\r
5090       MachineWhiteEvent();\r
5091       /*\r
5092        * refresh the tags dialog only if it's visible\r
5093        */\r
5094       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5095           char *tags;\r
5096           tags = PGNTags(&gameInfo);\r
5097           TagsPopUp(tags, CmailMsg());\r
5098           free(tags);\r
5099       }\r
5100       SAY("computer starts playing white");\r
5101       break;\r
5102 \r
5103     case IDM_MachineBlack:\r
5104       MachineBlackEvent();\r
5105       /*\r
5106        * refresh the tags dialog only if it's visible\r
5107        */\r
5108       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5109           char *tags;\r
5110           tags = PGNTags(&gameInfo);\r
5111           TagsPopUp(tags, CmailMsg());\r
5112           free(tags);\r
5113       }\r
5114       SAY("computer starts playing black");\r
5115       break;\r
5116 \r
5117     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5118       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5119       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5120       break;\r
5121 \r
5122     case IDM_TwoMachines:\r
5123       TwoMachinesEvent();\r
5124       /*\r
5125 \r
5126        * refresh the tags dialog only if it's visible\r
5127        */\r
5128       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5129           char *tags;\r
5130           tags = PGNTags(&gameInfo);\r
5131           TagsPopUp(tags, CmailMsg());\r
5132           free(tags);\r
5133       }\r
5134       SAY("computer starts playing both sides");\r
5135       break;\r
5136 \r
5137     case IDM_AnalysisMode:\r
5138       if(AnalyzeModeEvent()) {\r
5139         SAY("analyzing current position");\r
5140       }\r
5141       break;\r
5142 \r
5143     case IDM_AnalyzeFile:\r
5144       AnalyzeFileEvent();\r
5145       break;\r
5146 \r
5147     case IDM_IcsClient:\r
5148       IcsClientEvent();\r
5149       break;\r
5150 \r
5151     case IDM_EditGame:\r
5152     case IDM_EditGame2:\r
5153       EditGameEvent();\r
5154       SAY("edit game");\r
5155       break;\r
5156 \r
5157     case IDM_EditPosition:\r
5158     case IDM_EditPosition2:\r
5159       EditPositionEvent();\r
5160       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5161       break;\r
5162 \r
5163     case IDM_Training:\r
5164       TrainingEvent();\r
5165       break;\r
5166 \r
5167     case IDM_ShowGameList:\r
5168       ShowGameListProc();\r
5169       break;\r
5170 \r
5171     case IDM_EditProgs1:\r
5172       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5173       break;\r
5174 \r
5175     case IDM_LoadProg1:\r
5176      LoadEnginePopUp(hwndMain, 0);\r
5177       break;\r
5178 \r
5179     case IDM_LoadProg2:\r
5180      LoadEnginePopUp(hwndMain, 1);\r
5181       break;\r
5182 \r
5183     case IDM_EditServers:\r
5184       EditTagsPopUp(icsNames, &icsNames);\r
5185       break;\r
5186 \r
5187     case IDM_EditTags:\r
5188     case IDM_Tags:\r
5189       EditTagsProc();\r
5190       break;\r
5191 \r
5192     case IDM_EditBook:\r
5193       EditBookEvent();\r
5194       break;\r
5195 \r
5196     case IDM_EditComment:\r
5197     case IDM_Comment:\r
5198       if (commentUp && editComment) {\r
5199         CommentPopDown();\r
5200       } else {\r
5201         EditCommentEvent();\r
5202       }\r
5203       break;\r
5204 \r
5205     case IDM_Pause:\r
5206       PauseEvent();\r
5207       break;\r
5208 \r
5209     case IDM_Accept:\r
5210       AcceptEvent();\r
5211       break;\r
5212 \r
5213     case IDM_Decline:\r
5214       DeclineEvent();\r
5215       break;\r
5216 \r
5217     case IDM_Rematch:\r
5218 \r
5219       RematchEvent();\r
5220       break;\r
5221 \r
5222     case IDM_CallFlag:\r
5223       CallFlagEvent();\r
5224       break;\r
5225 \r
5226     case IDM_Draw:\r
5227       DrawEvent();\r
5228       break;\r
5229 \r
5230     case IDM_Adjourn:\r
5231       AdjournEvent();\r
5232       break;\r
5233 \r
5234     case IDM_Abort:\r
5235       AbortEvent();\r
5236       break;\r
5237 \r
5238     case IDM_Resign:\r
5239       ResignEvent();\r
5240       break;\r
5241 \r
5242     case IDM_StopObserving:\r
5243       StopObservingEvent();\r
5244       break;\r
5245 \r
5246     case IDM_StopExamining:\r
5247       StopExaminingEvent();\r
5248       break;\r
5249 \r
5250     case IDM_Upload:\r
5251       UploadGameEvent();\r
5252       break;\r
5253 \r
5254     case IDM_TypeInMove:\r
5255       TypeInEvent('\000');\r
5256       break;\r
5257 \r
5258     case IDM_TypeInName:\r
5259       PopUpNameDialog('\000');\r
5260       break;\r
5261 \r
5262     case IDM_Backward:\r
5263       BackwardEvent();\r
5264       SetFocus(hwndMain);\r
5265       break;\r
5266 \r
5267     JAWS_MENU_ITEMS\r
5268 \r
5269     case IDM_Forward:\r
5270       ForwardEvent();\r
5271       SetFocus(hwndMain);\r
5272       break;\r
5273 \r
5274     case IDM_ToStart:\r
5275       ToStartEvent();\r
5276       SetFocus(hwndMain);\r
5277       break;\r
5278 \r
5279     case IDM_ToEnd:\r
5280       ToEndEvent();\r
5281       SetFocus(hwndMain);\r
5282       break;\r
5283 \r
5284     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5285     case OPT_GameListPrev:\r
5286       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5287       break;\r
5288 \r
5289     case IDM_Revert:\r
5290       RevertEvent(FALSE);\r
5291       break;\r
5292 \r
5293     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5294       RevertEvent(TRUE);\r
5295       break;\r
5296 \r
5297     case IDM_TruncateGame:\r
5298       TruncateGameEvent();\r
5299       break;\r
5300 \r
5301     case IDM_MoveNow:\r
5302       MoveNowEvent();\r
5303       break;\r
5304 \r
5305     case IDM_RetractMove:\r
5306       RetractMoveEvent();\r
5307       break;\r
5308 \r
5309     case IDM_FlipView:\r
5310       flipView = !flipView;\r
5311       DrawPosition(FALSE, NULL);\r
5312       break;\r
5313 \r
5314     case IDM_FlipClock:\r
5315       flipClock = !flipClock;\r
5316       DisplayBothClocks();\r
5317       DisplayLogos();\r
5318       break;\r
5319 \r
5320     case IDM_MuteSounds:\r
5321       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5322       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5323                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5324       break;\r
5325 \r
5326     case IDM_GeneralOptions:\r
5327       GeneralOptionsPopup(hwnd);\r
5328       DrawPosition(TRUE, NULL);\r
5329       break;\r
5330 \r
5331     case IDM_BoardOptions:\r
5332       BoardOptionsPopup(hwnd);\r
5333       break;\r
5334 \r
5335     case IDM_ThemeOptions:\r
5336       ThemeOptionsPopup(hwnd);\r
5337       break;\r
5338 \r
5339     case IDM_EnginePlayOptions:\r
5340       EnginePlayOptionsPopup(hwnd);\r
5341       break;\r
5342 \r
5343     case IDM_Engine1Options:\r
5344       EngineOptionsPopup(hwnd, &first);\r
5345       break;\r
5346 \r
5347     case IDM_Engine2Options:\r
5348       savedHwnd = hwnd;\r
5349       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5350       EngineOptionsPopup(hwnd, &second);\r
5351       break;\r
5352 \r
5353     case IDM_OptionsUCI:\r
5354       UciOptionsPopup(hwnd);\r
5355       break;\r
5356 \r
5357     case IDM_Tourney:\r
5358       TourneyPopup(hwnd);\r
5359       break;\r
5360 \r
5361     case IDM_IcsOptions:\r
5362       IcsOptionsPopup(hwnd);\r
5363       break;\r
5364 \r
5365     case IDM_Fonts:\r
5366       FontsOptionsPopup(hwnd);\r
5367       break;\r
5368 \r
5369     case IDM_Sounds:\r
5370       SoundOptionsPopup(hwnd);\r
5371       break;\r
5372 \r
5373     case IDM_CommPort:\r
5374       CommPortOptionsPopup(hwnd);\r
5375       break;\r
5376 \r
5377     case IDM_LoadOptions:\r
5378       LoadOptionsPopup(hwnd);\r
5379       break;\r
5380 \r
5381     case IDM_SaveOptions:\r
5382       SaveOptionsPopup(hwnd);\r
5383       break;\r
5384 \r
5385     case IDM_TimeControl:\r
5386       TimeControlOptionsPopup(hwnd);\r
5387       break;\r
5388 \r
5389     case IDM_SaveSettings:\r
5390       SaveSettings(settingsFileName);\r
5391       break;\r
5392 \r
5393     case IDM_SaveSettingsOnExit:\r
5394       saveSettingsOnExit = !saveSettingsOnExit;\r
5395       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5396                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5397                                          MF_CHECKED : MF_UNCHECKED));\r
5398       break;\r
5399 \r
5400     case IDM_Hint:\r
5401       HintEvent();\r
5402       break;\r
5403 \r
5404     case IDM_Book:\r
5405       BookEvent();\r
5406       break;\r
5407 \r
5408     case IDM_AboutGame:\r
5409       AboutGameEvent();\r
5410       break;\r
5411 \r
5412     case IDM_Debug:\r
5413       appData.debugMode = !appData.debugMode;\r
5414       if (appData.debugMode) {\r
5415         char dir[MSG_SIZ];\r
5416         GetCurrentDirectory(MSG_SIZ, dir);\r
5417         SetCurrentDirectory(installDir);\r
5418         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5419         SetCurrentDirectory(dir);\r
5420         setbuf(debugFP, NULL);\r
5421       } else {\r
5422         fclose(debugFP);\r
5423         debugFP = NULL;\r
5424       }\r
5425       break;\r
5426 \r
5427     case IDM_HELPCONTENTS:\r
5428       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5429           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5430           MessageBox (GetFocus(),\r
5431                     _("Unable to activate help"),\r
5432                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5433       }\r
5434       break;\r
5435 \r
5436     case IDM_HELPSEARCH:\r
5437         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5438             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5439         MessageBox (GetFocus(),\r
5440                     _("Unable to activate help"),\r
5441                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5442       }\r
5443       break;\r
5444 \r
5445     case IDM_HELPHELP:\r
5446       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5447         MessageBox (GetFocus(),\r
5448                     _("Unable to activate help"),\r
5449                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5450       }\r
5451       break;\r
5452 \r
5453     case IDM_ABOUT:\r
5454       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5455       DialogBox(hInst, \r
5456         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5457         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5458       FreeProcInstance(lpProc);\r
5459       break;\r
5460 \r
5461     case IDM_DirectCommand1:\r
5462       AskQuestionEvent(_("Direct Command"),\r
5463                        _("Send to chess program:"), "", "1");\r
5464       break;\r
5465     case IDM_DirectCommand2:\r
5466       AskQuestionEvent(_("Direct Command"),\r
5467                        _("Send to second chess program:"), "", "2");\r
5468       break;\r
5469 \r
5470     case EP_WhitePawn:\r
5471       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_WhiteKnight:\r
5476       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_WhiteBishop:\r
5481       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_WhiteRook:\r
5486       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_WhiteQueen:\r
5491       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_WhiteFerz:\r
5496       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_WhiteWazir:\r
5501       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_WhiteAlfil:\r
5506       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_WhiteCannon:\r
5511       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_WhiteCardinal:\r
5516       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_WhiteMarshall:\r
5521       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_WhiteKing:\r
5526       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_BlackPawn:\r
5531       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_BlackKnight:\r
5536       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_BlackBishop:\r
5541       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case EP_BlackRook:\r
5546       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case EP_BlackQueen:\r
5551       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case EP_BlackFerz:\r
5556       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case EP_BlackWazir:\r
5561       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case EP_BlackAlfil:\r
5566       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case EP_BlackCannon:\r
5571       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5572       fromX = fromY = -1;\r
5573       break;\r
5574 \r
5575     case EP_BlackCardinal:\r
5576       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5577       fromX = fromY = -1;\r
5578       break;\r
5579 \r
5580     case EP_BlackMarshall:\r
5581       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5582       fromX = fromY = -1;\r
5583       break;\r
5584 \r
5585     case EP_BlackKing:\r
5586       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5587       fromX = fromY = -1;\r
5588       break;\r
5589 \r
5590     case EP_EmptySquare:\r
5591       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5592       fromX = fromY = -1;\r
5593       break;\r
5594 \r
5595     case EP_ClearBoard:\r
5596       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5597       fromX = fromY = -1;\r
5598       break;\r
5599 \r
5600     case EP_White:\r
5601       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5602       fromX = fromY = -1;\r
5603       break;\r
5604 \r
5605     case EP_Black:\r
5606       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5607       fromX = fromY = -1;\r
5608       break;\r
5609 \r
5610     case EP_Promote:\r
5611       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5612       fromX = fromY = -1;\r
5613       break;\r
5614 \r
5615     case EP_Demote:\r
5616       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5617       fromX = fromY = -1;\r
5618       break;\r
5619 \r
5620     case DP_Pawn:\r
5621       DropMenuEvent(WhitePawn, fromX, fromY);\r
5622       fromX = fromY = -1;\r
5623       break;\r
5624 \r
5625     case DP_Knight:\r
5626       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5627       fromX = fromY = -1;\r
5628       break;\r
5629 \r
5630     case DP_Bishop:\r
5631       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5632       fromX = fromY = -1;\r
5633       break;\r
5634 \r
5635     case DP_Rook:\r
5636       DropMenuEvent(WhiteRook, fromX, fromY);\r
5637       fromX = fromY = -1;\r
5638       break;\r
5639 \r
5640     case DP_Queen:\r
5641       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5642       fromX = fromY = -1;\r
5643       break;\r
5644 \r
5645     case IDM_English:\r
5646       barbaric = 0; appData.language = "";\r
5647       TranslateMenus(0);\r
5648       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5649       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5650       lastChecked = wmId;\r
5651       break;\r
5652 \r
5653     default:\r
5654       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5655           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5656       else\r
5657       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5658           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5659           TranslateMenus(0);\r
5660           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5661           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5662           lastChecked = wmId;\r
5663           break;\r
5664       }\r
5665       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5666     }\r
5667     break;\r
5668 \r
5669   case WM_TIMER:\r
5670     switch (wParam) {\r
5671     case CLOCK_TIMER_ID:\r
5672       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5673       clockTimerEvent = 0;\r
5674       DecrementClocks(); /* call into back end */\r
5675       break;\r
5676     case LOAD_GAME_TIMER_ID:\r
5677       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5678       loadGameTimerEvent = 0;\r
5679       AutoPlayGameLoop(); /* call into back end */\r
5680       break;\r
5681     case ANALYSIS_TIMER_ID:\r
5682       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5683                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5684         AnalysisPeriodicEvent(0);\r
5685       } else {\r
5686         KillTimer(hwnd, analysisTimerEvent);\r
5687         analysisTimerEvent = 0;\r
5688       }\r
5689       break;\r
5690     case DELAYED_TIMER_ID:\r
5691       KillTimer(hwnd, delayedTimerEvent);\r
5692       delayedTimerEvent = 0;\r
5693       delayedTimerCallback();\r
5694       break;\r
5695     }\r
5696     break;\r
5697 \r
5698   case WM_USER_Input:\r
5699     InputEvent(hwnd, message, wParam, lParam);\r
5700     break;\r
5701 \r
5702   /* [AS] Also move "attached" child windows */\r
5703   case WM_WINDOWPOSCHANGING:\r
5704 \r
5705     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5706         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5707 \r
5708         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5709             /* Window is moving */\r
5710             RECT rcMain;\r
5711 \r
5712 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5713             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5714             rcMain.right  = wpMain.x + wpMain.width;\r
5715             rcMain.top    = wpMain.y;\r
5716             rcMain.bottom = wpMain.y + wpMain.height;\r
5717             \r
5718             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5719             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5720             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5721             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5722             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5723             wpMain.x = lpwp->x;\r
5724             wpMain.y = lpwp->y;\r
5725         }\r
5726     }\r
5727     break;\r
5728 \r
5729   /* [AS] Snapping */\r
5730   case WM_ENTERSIZEMOVE:\r
5731     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5732     if (hwnd == hwndMain) {\r
5733       doingSizing = TRUE;\r
5734       lastSizing = 0;\r
5735     }\r
5736     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5737     break;\r
5738 \r
5739   case WM_SIZING:\r
5740     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5741     if (hwnd == hwndMain) {\r
5742       lastSizing = wParam;\r
5743     }\r
5744     break;\r
5745 \r
5746   case WM_MOVING:\r
5747     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5748       return OnMoving( &sd, hwnd, wParam, lParam );\r
5749 \r
5750   case WM_EXITSIZEMOVE:\r
5751     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5752     if (hwnd == hwndMain) {\r
5753       RECT client;\r
5754       doingSizing = FALSE;\r
5755       InvalidateRect(hwnd, &boardRect, FALSE);\r
5756       GetClientRect(hwnd, &client);\r
5757       ResizeBoard(client.right, client.bottom, lastSizing);\r
5758       lastSizing = 0;\r
5759       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5760     }\r
5761     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5762     break;\r
5763 \r
5764   case WM_DESTROY: /* message: window being destroyed */\r
5765     PostQuitMessage(0);\r
5766     break;\r
5767 \r
5768   case WM_CLOSE:\r
5769     if (hwnd == hwndMain) {\r
5770       ExitEvent(0);\r
5771     }\r
5772     break;\r
5773 \r
5774   default:      /* Passes it on if unprocessed */\r
5775     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5776   }\r
5777 \r
5778 \r
5779   return 0;\r
5780 }\r
5781 \r
5782 /*---------------------------------------------------------------------------*\\r
5783  *\r
5784  * Misc utility routines\r
5785  *\r
5786 \*---------------------------------------------------------------------------*/\r
5787 \r
5788 /*\r
5789  * Decent random number generator, at least not as bad as Windows\r
5790  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5791  */\r
5792 unsigned int randstate;\r
5793 \r
5794 int\r
5795 myrandom(void)\r
5796 {\r
5797   randstate = randstate * 1664525 + 1013904223;\r
5798   return (int) randstate & 0x7fffffff;\r
5799 }\r
5800 \r
5801 void\r
5802 mysrandom(unsigned int seed)\r
5803 {\r
5804   randstate = seed;\r
5805 }\r
5806 \r
5807 \r
5808 /* \r
5809  * returns TRUE if user selects a different color, FALSE otherwise \r
5810  */\r
5811 \r
5812 BOOL\r
5813 ChangeColor(HWND hwnd, COLORREF *which)\r
5814 {\r
5815   static BOOL firstTime = TRUE;\r
5816   static DWORD customColors[16];\r
5817   CHOOSECOLOR cc;\r
5818   COLORREF newcolor;\r
5819   int i;\r
5820   ColorClass ccl;\r
5821 \r
5822   if (firstTime) {\r
5823     /* Make initial colors in use available as custom colors */\r
5824     /* Should we put the compiled-in defaults here instead? */\r
5825     i = 0;\r
5826     customColors[i++] = lightSquareColor & 0xffffff;\r
5827     customColors[i++] = darkSquareColor & 0xffffff;\r
5828     customColors[i++] = whitePieceColor & 0xffffff;\r
5829     customColors[i++] = blackPieceColor & 0xffffff;\r
5830     customColors[i++] = highlightSquareColor & 0xffffff;\r
5831     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5832 \r
5833     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5834       customColors[i++] = textAttribs[ccl].color;\r
5835     }\r
5836     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5837     firstTime = FALSE;\r
5838   }\r
5839 \r
5840   cc.lStructSize = sizeof(cc);\r
5841   cc.hwndOwner = hwnd;\r
5842   cc.hInstance = NULL;\r
5843   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5844   cc.lpCustColors = (LPDWORD) customColors;\r
5845   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5846 \r
5847   if (!ChooseColor(&cc)) return FALSE;\r
5848 \r
5849   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5850   if (newcolor == *which) return FALSE;\r
5851   *which = newcolor;\r
5852   return TRUE;\r
5853 \r
5854   /*\r
5855   InitDrawingColors();\r
5856   InvalidateRect(hwnd, &boardRect, FALSE);\r
5857   */\r
5858 }\r
5859 \r
5860 BOOLEAN\r
5861 MyLoadSound(MySound *ms)\r
5862 {\r
5863   BOOL ok = FALSE;\r
5864   struct stat st;\r
5865   FILE *f;\r
5866 \r
5867   if (ms->data && ms->flag) free(ms->data);\r
5868   ms->data = NULL;\r
5869 \r
5870   switch (ms->name[0]) {\r
5871   case NULLCHAR:\r
5872     /* Silence */\r
5873     ok = TRUE;\r
5874     break;\r
5875   case '$':\r
5876     /* System sound from Control Panel.  Don't preload here. */\r
5877     ok = TRUE;\r
5878     break;\r
5879   case '!':\r
5880     if (ms->name[1] == NULLCHAR) {\r
5881       /* "!" alone = silence */\r
5882       ok = TRUE;\r
5883     } else {\r
5884       /* Builtin wave resource.  Error if not found. */\r
5885       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5886       if (h == NULL) break;\r
5887       ms->data = (void *)LoadResource(hInst, h);\r
5888       ms->flag = 0; // not maloced, so cannot be freed!\r
5889       if (h == NULL) break;\r
5890       ok = TRUE;\r
5891     }\r
5892     break;\r
5893   default:\r
5894     /* .wav file.  Error if not found. */\r
5895     f = fopen(ms->name, "rb");\r
5896     if (f == NULL) break;\r
5897     if (fstat(fileno(f), &st) < 0) break;\r
5898     ms->data = malloc(st.st_size);\r
5899     ms->flag = 1;\r
5900     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5901     fclose(f);\r
5902     ok = TRUE;\r
5903     break;\r
5904   }\r
5905   if (!ok) {\r
5906     char buf[MSG_SIZ];\r
5907       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5908     DisplayError(buf, GetLastError());\r
5909   }\r
5910   return ok;\r
5911 }\r
5912 \r
5913 BOOLEAN\r
5914 MyPlaySound(MySound *ms)\r
5915 {\r
5916   BOOLEAN ok = FALSE;\r
5917 \r
5918   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5919   switch (ms->name[0]) {\r
5920   case NULLCHAR:\r
5921         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5922     /* Silence */\r
5923     ok = TRUE;\r
5924     break;\r
5925   case '$':\r
5926     /* System sound from Control Panel (deprecated feature).\r
5927        "$" alone or an unset sound name gets default beep (still in use). */\r
5928     if (ms->name[1]) {\r
5929       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5930     }\r
5931     if (!ok) ok = MessageBeep(MB_OK);\r
5932     break; \r
5933   case '!':\r
5934     /* Builtin wave resource, or "!" alone for silence */\r
5935     if (ms->name[1]) {\r
5936       if (ms->data == NULL) return FALSE;\r
5937       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5938     } else {\r
5939       ok = TRUE;\r
5940     }\r
5941     break;\r
5942   default:\r
5943     /* .wav file.  Error if not found. */\r
5944     if (ms->data == NULL) return FALSE;\r
5945     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5946     break;\r
5947   }\r
5948   /* Don't print an error: this can happen innocently if the sound driver\r
5949      is busy; for instance, if another instance of WinBoard is playing\r
5950      a sound at about the same time. */\r
5951   return ok;\r
5952 }\r
5953 \r
5954 \r
5955 LRESULT CALLBACK\r
5956 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5957 {\r
5958   BOOL ok;\r
5959   OPENFILENAME *ofn;\r
5960   static UINT *number; /* gross that this is static */\r
5961 \r
5962   switch (message) {\r
5963   case WM_INITDIALOG: /* message: initialize dialog box */\r
5964     /* Center the dialog over the application window */\r
5965     ofn = (OPENFILENAME *) lParam;\r
5966     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5967       number = (UINT *) ofn->lCustData;\r
5968       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5969     } else {\r
5970       number = NULL;\r
5971     }\r
5972     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5973     Translate(hDlg, 1536);\r
5974     return FALSE;  /* Allow for further processing */\r
5975 \r
5976   case WM_COMMAND:\r
5977     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5978       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5979     }\r
5980     return FALSE;  /* Allow for further processing */\r
5981   }\r
5982   return FALSE;\r
5983 }\r
5984 \r
5985 UINT APIENTRY\r
5986 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5987 {\r
5988   static UINT *number;\r
5989   OPENFILENAME *ofname;\r
5990   OFNOTIFY *ofnot;\r
5991   switch (uiMsg) {\r
5992   case WM_INITDIALOG:\r
5993     Translate(hdlg, DLG_IndexNumber);\r
5994     ofname = (OPENFILENAME *)lParam;\r
5995     number = (UINT *)(ofname->lCustData);\r
5996     break;\r
5997   case WM_NOTIFY:\r
5998     ofnot = (OFNOTIFY *)lParam;\r
5999     if (ofnot->hdr.code == CDN_FILEOK) {\r
6000       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6001     }\r
6002     break;\r
6003   }\r
6004   return 0;\r
6005 }\r
6006 \r
6007 \r
6008 FILE *\r
6009 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6010                char *nameFilt, char *dlgTitle, UINT *number,\r
6011                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6012 {\r
6013   OPENFILENAME openFileName;\r
6014   char buf1[MSG_SIZ];\r
6015   FILE *f;\r
6016 \r
6017   if (fileName == NULL) fileName = buf1;\r
6018   if (defName == NULL) {\r
6019     safeStrCpy(fileName, "*.", 3 );\r
6020     strcat(fileName, defExt);\r
6021   } else {\r
6022     safeStrCpy(fileName, defName, MSG_SIZ );\r
6023   }\r
6024     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6025   if (number) *number = 0;\r
6026 \r
6027   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6028   openFileName.hwndOwner         = hwnd;\r
6029   openFileName.hInstance         = (HANDLE) hInst;\r
6030   openFileName.lpstrFilter       = nameFilt;\r
6031   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6032   openFileName.nMaxCustFilter    = 0L;\r
6033   openFileName.nFilterIndex      = 1L;\r
6034   openFileName.lpstrFile         = fileName;\r
6035   openFileName.nMaxFile          = MSG_SIZ;\r
6036   openFileName.lpstrFileTitle    = fileTitle;\r
6037   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6038   openFileName.lpstrInitialDir   = NULL;\r
6039   openFileName.lpstrTitle        = dlgTitle;\r
6040   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6041     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6042     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6043     | (oldDialog ? 0 : OFN_EXPLORER);\r
6044   openFileName.nFileOffset       = 0;\r
6045   openFileName.nFileExtension    = 0;\r
6046   openFileName.lpstrDefExt       = defExt;\r
6047   openFileName.lCustData         = (LONG) number;\r
6048   openFileName.lpfnHook          = oldDialog ?\r
6049     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6050   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6051 \r
6052   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6053                         GetOpenFileName(&openFileName)) {\r
6054     /* open the file */\r
6055     f = fopen(openFileName.lpstrFile, write);\r
6056     if (f == NULL) {\r
6057       MessageBox(hwnd, _("File open failed"), NULL,\r
6058                  MB_OK|MB_ICONEXCLAMATION);\r
6059       return NULL;\r
6060     }\r
6061   } else {\r
6062     int err = CommDlgExtendedError();\r
6063     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6064     return FALSE;\r
6065   }\r
6066   return f;\r
6067 }\r
6068 \r
6069 \r
6070 \r
6071 VOID APIENTRY\r
6072 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6073 {\r
6074   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6075 \r
6076   /*\r
6077    * Get the first pop-up menu in the menu template. This is the\r
6078    * menu that TrackPopupMenu displays.\r
6079    */\r
6080   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6081   TranslateOneMenu(10, hmenuTrackPopup);\r
6082 \r
6083   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6084 \r
6085   /*\r
6086    * TrackPopup uses screen coordinates, so convert the\r
6087    * coordinates of the mouse click to screen coordinates.\r
6088    */\r
6089   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6090 \r
6091   /* Draw and track the floating pop-up menu. */\r
6092   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6093                  pt.x, pt.y, 0, hwnd, NULL);\r
6094 \r
6095   /* Destroy the menu.*/\r
6096   DestroyMenu(hmenu);\r
6097 }\r
6098    \r
6099 typedef struct {\r
6100   HWND hDlg, hText;\r
6101   int sizeX, sizeY, newSizeX, newSizeY;\r
6102   HDWP hdwp;\r
6103 } ResizeEditPlusButtonsClosure;\r
6104 \r
6105 BOOL CALLBACK\r
6106 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6107 {\r
6108   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6109   RECT rect;\r
6110   POINT pt;\r
6111 \r
6112   if (hChild == cl->hText) return TRUE;\r
6113   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6114   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6115   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6116   ScreenToClient(cl->hDlg, &pt);\r
6117   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6118     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6119   return TRUE;\r
6120 }\r
6121 \r
6122 /* Resize a dialog that has a (rich) edit field filling most of\r
6123    the top, with a row of buttons below */\r
6124 VOID\r
6125 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6126 {\r
6127   RECT rectText;\r
6128   int newTextHeight, newTextWidth;\r
6129   ResizeEditPlusButtonsClosure cl;\r
6130   \r
6131   /*if (IsIconic(hDlg)) return;*/\r
6132   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6133   \r
6134   cl.hdwp = BeginDeferWindowPos(8);\r
6135 \r
6136   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6137   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6138   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6139   if (newTextHeight < 0) {\r
6140     newSizeY += -newTextHeight;\r
6141     newTextHeight = 0;\r
6142   }\r
6143   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6144     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6145 \r
6146   cl.hDlg = hDlg;\r
6147   cl.hText = hText;\r
6148   cl.sizeX = sizeX;\r
6149   cl.sizeY = sizeY;\r
6150   cl.newSizeX = newSizeX;\r
6151   cl.newSizeY = newSizeY;\r
6152   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6153 \r
6154   EndDeferWindowPos(cl.hdwp);\r
6155 }\r
6156 \r
6157 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6158 {\r
6159     RECT    rChild, rParent;\r
6160     int     wChild, hChild, wParent, hParent;\r
6161     int     wScreen, hScreen, xNew, yNew;\r
6162     HDC     hdc;\r
6163 \r
6164     /* Get the Height and Width of the child window */\r
6165     GetWindowRect (hwndChild, &rChild);\r
6166     wChild = rChild.right - rChild.left;\r
6167     hChild = rChild.bottom - rChild.top;\r
6168 \r
6169     /* Get the Height and Width of the parent window */\r
6170     GetWindowRect (hwndParent, &rParent);\r
6171     wParent = rParent.right - rParent.left;\r
6172     hParent = rParent.bottom - rParent.top;\r
6173 \r
6174     /* Get the display limits */\r
6175     hdc = GetDC (hwndChild);\r
6176     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6177     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6178     ReleaseDC(hwndChild, hdc);\r
6179 \r
6180     /* Calculate new X position, then adjust for screen */\r
6181     xNew = rParent.left + ((wParent - wChild) /2);\r
6182     if (xNew < 0) {\r
6183         xNew = 0;\r
6184     } else if ((xNew+wChild) > wScreen) {\r
6185         xNew = wScreen - wChild;\r
6186     }\r
6187 \r
6188     /* Calculate new Y position, then adjust for screen */\r
6189     if( mode == 0 ) {\r
6190         yNew = rParent.top  + ((hParent - hChild) /2);\r
6191     }\r
6192     else {\r
6193         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6194     }\r
6195 \r
6196     if (yNew < 0) {\r
6197         yNew = 0;\r
6198     } else if ((yNew+hChild) > hScreen) {\r
6199         yNew = hScreen - hChild;\r
6200     }\r
6201 \r
6202     /* Set it, and return */\r
6203     return SetWindowPos (hwndChild, NULL,\r
6204                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6205 }\r
6206 \r
6207 /* Center one window over another */\r
6208 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6209 {\r
6210     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6211 }\r
6212 \r
6213 /*---------------------------------------------------------------------------*\\r
6214  *\r
6215  * Startup Dialog functions\r
6216  *\r
6217 \*---------------------------------------------------------------------------*/\r
6218 void\r
6219 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6220 {\r
6221   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6222 \r
6223   while (*cd != NULL) {\r
6224     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6225     cd++;\r
6226   }\r
6227 }\r
6228 \r
6229 void\r
6230 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6231 {\r
6232   char buf1[MAX_ARG_LEN];\r
6233   int len;\r
6234 \r
6235   if (str[0] == '@') {\r
6236     FILE* f = fopen(str + 1, "r");\r
6237     if (f == NULL) {\r
6238       DisplayFatalError(str + 1, errno, 2);\r
6239       return;\r
6240     }\r
6241     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6242     fclose(f);\r
6243     buf1[len] = NULLCHAR;\r
6244     str = buf1;\r
6245   }\r
6246 \r
6247   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6248 \r
6249   for (;;) {\r
6250     char buf[MSG_SIZ];\r
6251     char *end = strchr(str, '\n');\r
6252     if (end == NULL) return;\r
6253     memcpy(buf, str, end - str);\r
6254     buf[end - str] = NULLCHAR;\r
6255     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6256     str = end + 1;\r
6257   }\r
6258 }\r
6259 \r
6260 void\r
6261 SetStartupDialogEnables(HWND hDlg)\r
6262 {\r
6263   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6264     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6265     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6266   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6267     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6268   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6269     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6270   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6271     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6272   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6273     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6274     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6275     IsDlgButtonChecked(hDlg, OPT_View));\r
6276 }\r
6277 \r
6278 char *\r
6279 QuoteForFilename(char *filename)\r
6280 {\r
6281   int dquote, space;\r
6282   dquote = strchr(filename, '"') != NULL;\r
6283   space = strchr(filename, ' ') != NULL;\r
6284   if (dquote || space) {\r
6285     if (dquote) {\r
6286       return "'";\r
6287     } else {\r
6288       return "\"";\r
6289     }\r
6290   } else {\r
6291     return "";\r
6292   }\r
6293 }\r
6294 \r
6295 VOID\r
6296 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6297 {\r
6298   char buf[MSG_SIZ];\r
6299   char *q;\r
6300 \r
6301   InitComboStringsFromOption(hwndCombo, nthnames);\r
6302   q = QuoteForFilename(nthcp);\r
6303     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6304   if (*nthdir != NULLCHAR) {\r
6305     q = QuoteForFilename(nthdir);\r
6306       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6307   }\r
6308   if (*nthcp == NULLCHAR) {\r
6309     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6310   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6311     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6312     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6313   }\r
6314 }\r
6315 \r
6316 LRESULT CALLBACK\r
6317 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6318 {\r
6319   char buf[MSG_SIZ];\r
6320   HANDLE hwndCombo;\r
6321   char *p;\r
6322 \r
6323   switch (message) {\r
6324   case WM_INITDIALOG:\r
6325     /* Center the dialog */\r
6326     CenterWindow (hDlg, GetDesktopWindow());\r
6327     Translate(hDlg, DLG_Startup);\r
6328     /* Initialize the dialog items */\r
6329     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6330                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6331                   firstChessProgramNames);\r
6332     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6333                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6334                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6335     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6336     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6337       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6338     if (*appData.icsHelper != NULLCHAR) {\r
6339       char *q = QuoteForFilename(appData.icsHelper);\r
6340       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6341     }\r
6342     if (*appData.icsHost == NULLCHAR) {\r
6343       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6344       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6345     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6346       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6347       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6348     }\r
6349 \r
6350     if (appData.icsActive) {\r
6351       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6352     }\r
6353     else if (appData.noChessProgram) {\r
6354       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6355     }\r
6356     else {\r
6357       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6358     }\r
6359 \r
6360     SetStartupDialogEnables(hDlg);\r
6361     return TRUE;\r
6362 \r
6363   case WM_COMMAND:\r
6364     switch (LOWORD(wParam)) {\r
6365     case IDOK:\r
6366       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6367         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6368         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6369         p = buf;\r
6370         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6371         ParseArgs(StringGet, &p);\r
6372         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6373         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6374         p = buf;\r
6375         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6376         ParseArgs(StringGet, &p);\r
6377         SwapEngines(singleList); // ... and then make it 'second'\r
6378 \r
6379         appData.noChessProgram = FALSE;\r
6380         appData.icsActive = FALSE;\r
6381       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6382         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6383         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6384         p = buf;\r
6385         ParseArgs(StringGet, &p);\r
6386         if (appData.zippyPlay) {\r
6387           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6388           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6389           p = buf;\r
6390           ParseArgs(StringGet, &p);\r
6391         }\r
6392       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6393         appData.noChessProgram = TRUE;\r
6394         appData.icsActive = FALSE;\r
6395       } else {\r
6396         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6397                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6398         return TRUE;\r
6399       }\r
6400       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6401         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6402         p = buf;\r
6403         ParseArgs(StringGet, &p);\r
6404       }\r
6405       EndDialog(hDlg, TRUE);\r
6406       return TRUE;\r
6407 \r
6408     case IDCANCEL:\r
6409       ExitEvent(0);\r
6410       return TRUE;\r
6411 \r
6412     case IDM_HELPCONTENTS:\r
6413       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6414         MessageBox (GetFocus(),\r
6415                     _("Unable to activate help"),\r
6416                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6417       }\r
6418       break;\r
6419 \r
6420     default:\r
6421       SetStartupDialogEnables(hDlg);\r
6422       break;\r
6423     }\r
6424     break;\r
6425   }\r
6426   return FALSE;\r
6427 }\r
6428 \r
6429 /*---------------------------------------------------------------------------*\\r
6430  *\r
6431  * About box dialog functions\r
6432  *\r
6433 \*---------------------------------------------------------------------------*/\r
6434 \r
6435 /* Process messages for "About" dialog box */\r
6436 LRESULT CALLBACK\r
6437 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6438 {\r
6439   switch (message) {\r
6440   case WM_INITDIALOG: /* message: initialize dialog box */\r
6441     /* Center the dialog over the application window */\r
6442     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6443     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6444     Translate(hDlg, ABOUTBOX);\r
6445     JAWS_COPYRIGHT\r
6446     return (TRUE);\r
6447 \r
6448   case WM_COMMAND: /* message: received a command */\r
6449     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6450         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6451       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6452       return (TRUE);\r
6453     }\r
6454     break;\r
6455   }\r
6456   return (FALSE);\r
6457 }\r
6458 \r
6459 /*---------------------------------------------------------------------------*\\r
6460  *\r
6461  * Comment Dialog functions\r
6462  *\r
6463 \*---------------------------------------------------------------------------*/\r
6464 \r
6465 LRESULT CALLBACK\r
6466 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6467 {\r
6468   static HANDLE hwndText = NULL;\r
6469   int len, newSizeX, newSizeY;\r
6470   static int sizeX, sizeY;\r
6471   char *str;\r
6472   RECT rect;\r
6473   MINMAXINFO *mmi;\r
6474 \r
6475   switch (message) {\r
6476   case WM_INITDIALOG: /* message: initialize dialog box */\r
6477     /* Initialize the dialog items */\r
6478     Translate(hDlg, DLG_EditComment);\r
6479     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6480     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6481     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6482     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6483     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6484     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6485     SetWindowText(hDlg, commentTitle);\r
6486     if (editComment) {\r
6487       SetFocus(hwndText);\r
6488     } else {\r
6489       SetFocus(GetDlgItem(hDlg, IDOK));\r
6490     }\r
6491     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6492                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6493                 MAKELPARAM(FALSE, 0));\r
6494     /* Size and position the dialog */\r
6495     if (!commentDialog) {\r
6496       commentDialog = hDlg;\r
6497       GetClientRect(hDlg, &rect);\r
6498       sizeX = rect.right;\r
6499       sizeY = rect.bottom;\r
6500       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6501           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6502         WINDOWPLACEMENT wp;\r
6503         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6504         wp.length = sizeof(WINDOWPLACEMENT);\r
6505         wp.flags = 0;\r
6506         wp.showCmd = SW_SHOW;\r
6507         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6508         wp.rcNormalPosition.left = wpComment.x;\r
6509         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6510         wp.rcNormalPosition.top = wpComment.y;\r
6511         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6512         SetWindowPlacement(hDlg, &wp);\r
6513 \r
6514         GetClientRect(hDlg, &rect);\r
6515         newSizeX = rect.right;\r
6516         newSizeY = rect.bottom;\r
6517         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6518                               newSizeX, newSizeY);\r
6519         sizeX = newSizeX;\r
6520         sizeY = newSizeY;\r
6521       }\r
6522     }\r
6523     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6524     return FALSE;\r
6525 \r
6526   case WM_COMMAND: /* message: received a command */\r
6527     switch (LOWORD(wParam)) {\r
6528     case IDOK:\r
6529       if (editComment) {\r
6530         char *p, *q;\r
6531         /* Read changed options from the dialog box */\r
6532         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6533         len = GetWindowTextLength(hwndText);\r
6534         str = (char *) malloc(len + 1);\r
6535         GetWindowText(hwndText, str, len + 1);\r
6536         p = q = str;\r
6537         while (*q) {\r
6538           if (*q == '\r')\r
6539             q++;\r
6540           else\r
6541             *p++ = *q++;\r
6542         }\r
6543         *p = NULLCHAR;\r
6544         ReplaceComment(commentIndex, str);\r
6545         free(str);\r
6546       }\r
6547       CommentPopDown();\r
6548       return TRUE;\r
6549 \r
6550     case IDCANCEL:\r
6551     case OPT_CancelComment:\r
6552       CommentPopDown();\r
6553       return TRUE;\r
6554 \r
6555     case OPT_ClearComment:\r
6556       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6557       break;\r
6558 \r
6559     case OPT_EditComment:\r
6560       EditCommentEvent();\r
6561       return TRUE;\r
6562 \r
6563     default:\r
6564       break;\r
6565     }\r
6566     break;\r
6567 \r
6568   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6569         if( wParam == OPT_CommentText ) {\r
6570             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6571 \r
6572             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6573                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6574                 POINTL pt;\r
6575                 LRESULT index;\r
6576 \r
6577                 pt.x = LOWORD( lpMF->lParam );\r
6578                 pt.y = HIWORD( lpMF->lParam );\r
6579 \r
6580                 if(lpMF->msg == WM_CHAR) {\r
6581                         CHARRANGE sel;\r
6582                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6583                         index = sel.cpMin;\r
6584                 } else\r
6585                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6586 \r
6587                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6588                 len = GetWindowTextLength(hwndText);\r
6589                 str = (char *) malloc(len + 1);\r
6590                 GetWindowText(hwndText, str, len + 1);\r
6591                 ReplaceComment(commentIndex, str);\r
6592                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6593                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6594                 free(str);\r
6595 \r
6596                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6597                 lpMF->msg = WM_USER;\r
6598 \r
6599                 return TRUE;\r
6600             }\r
6601         }\r
6602         break;\r
6603 \r
6604   case WM_SIZE:\r
6605     newSizeX = LOWORD(lParam);\r
6606     newSizeY = HIWORD(lParam);\r
6607     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6608     sizeX = newSizeX;\r
6609     sizeY = newSizeY;\r
6610     break;\r
6611 \r
6612   case WM_GETMINMAXINFO:\r
6613     /* Prevent resizing window too small */\r
6614     mmi = (MINMAXINFO *) lParam;\r
6615     mmi->ptMinTrackSize.x = 100;\r
6616     mmi->ptMinTrackSize.y = 100;\r
6617     break;\r
6618   }\r
6619   return FALSE;\r
6620 }\r
6621 \r
6622 VOID\r
6623 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6624 {\r
6625   FARPROC lpProc;\r
6626   char *p, *q;\r
6627 \r
6628   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6629 \r
6630   if (str == NULL) str = "";\r
6631   p = (char *) malloc(2 * strlen(str) + 2);\r
6632   q = p;\r
6633   while (*str) {\r
6634     if (*str == '\n') *q++ = '\r';\r
6635     *q++ = *str++;\r
6636   }\r
6637   *q = NULLCHAR;\r
6638   if (commentText != NULL) free(commentText);\r
6639 \r
6640   commentIndex = index;\r
6641   commentTitle = title;\r
6642   commentText = p;\r
6643   editComment = edit;\r
6644 \r
6645   if (commentDialog) {\r
6646     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6647     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6648   } else {\r
6649     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6650     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6651                  hwndMain, (DLGPROC)lpProc);\r
6652     FreeProcInstance(lpProc);\r
6653   }\r
6654   commentUp = TRUE;\r
6655 }\r
6656 \r
6657 \r
6658 /*---------------------------------------------------------------------------*\\r
6659  *\r
6660  * Type-in move dialog functions\r
6661  * \r
6662 \*---------------------------------------------------------------------------*/\r
6663 \r
6664 LRESULT CALLBACK\r
6665 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6666 {\r
6667   char move[MSG_SIZ];\r
6668   HWND hInput;\r
6669 \r
6670   switch (message) {\r
6671   case WM_INITDIALOG:\r
6672     move[0] = (char) lParam;\r
6673     move[1] = NULLCHAR;\r
6674     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6675     Translate(hDlg, DLG_TypeInMove);\r
6676     hInput = GetDlgItem(hDlg, OPT_Move);\r
6677     SetWindowText(hInput, move);\r
6678     SetFocus(hInput);\r
6679     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6680     return FALSE;\r
6681 \r
6682   case WM_COMMAND:\r
6683     switch (LOWORD(wParam)) {\r
6684     case IDOK:\r
6685 \r
6686       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6687       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6688       TypeInDoneEvent(move);\r
6689       EndDialog(hDlg, TRUE);\r
6690       return TRUE;\r
6691     case IDCANCEL:\r
6692       EndDialog(hDlg, FALSE);\r
6693       return TRUE;\r
6694     default:\r
6695       break;\r
6696     }\r
6697     break;\r
6698   }\r
6699   return FALSE;\r
6700 }\r
6701 \r
6702 VOID\r
6703 PopUpMoveDialog(char firstchar)\r
6704 {\r
6705     FARPROC lpProc;\r
6706 \r
6707       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6708       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6709         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6710       FreeProcInstance(lpProc);\r
6711 }\r
6712 \r
6713 /*---------------------------------------------------------------------------*\\r
6714  *\r
6715  * Type-in name dialog functions\r
6716  * \r
6717 \*---------------------------------------------------------------------------*/\r
6718 \r
6719 LRESULT CALLBACK\r
6720 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6721 {\r
6722   char move[MSG_SIZ];\r
6723   HWND hInput;\r
6724 \r
6725   switch (message) {\r
6726   case WM_INITDIALOG:\r
6727     move[0] = (char) lParam;\r
6728     move[1] = NULLCHAR;\r
6729     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6730     Translate(hDlg, DLG_TypeInName);\r
6731     hInput = GetDlgItem(hDlg, OPT_Name);\r
6732     SetWindowText(hInput, move);\r
6733     SetFocus(hInput);\r
6734     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6735     return FALSE;\r
6736 \r
6737   case WM_COMMAND:\r
6738     switch (LOWORD(wParam)) {\r
6739     case IDOK:\r
6740       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6741       appData.userName = strdup(move);\r
6742       SetUserLogo(); DisplayLogos();\r
6743       SetGameInfo();\r
6744       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6745         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6746         DisplayTitle(move);\r
6747       }\r
6748 \r
6749 \r
6750       EndDialog(hDlg, TRUE);\r
6751       return TRUE;\r
6752     case IDCANCEL:\r
6753       EndDialog(hDlg, FALSE);\r
6754       return TRUE;\r
6755     default:\r
6756       break;\r
6757     }\r
6758     break;\r
6759   }\r
6760   return FALSE;\r
6761 }\r
6762 \r
6763 VOID\r
6764 PopUpNameDialog(char firstchar)\r
6765 {\r
6766     FARPROC lpProc;\r
6767     \r
6768       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6769       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6770         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6771       FreeProcInstance(lpProc);\r
6772 }\r
6773 \r
6774 /*---------------------------------------------------------------------------*\\r
6775  *\r
6776  *  Error dialogs\r
6777  * \r
6778 \*---------------------------------------------------------------------------*/\r
6779 \r
6780 /* Nonmodal error box */\r
6781 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6782                              WPARAM wParam, LPARAM lParam);\r
6783 \r
6784 VOID\r
6785 ErrorPopUp(char *title, char *content)\r
6786 {\r
6787   FARPROC lpProc;\r
6788   char *p, *q;\r
6789   BOOLEAN modal = hwndMain == NULL;\r
6790 \r
6791   p = content;\r
6792   q = errorMessage;\r
6793   while (*p) {\r
6794     if (*p == '\n') {\r
6795       if (modal) {\r
6796         *q++ = ' ';\r
6797         p++;\r
6798       } else {\r
6799         *q++ = '\r';\r
6800         *q++ = *p++;\r
6801       }\r
6802     } else {\r
6803       *q++ = *p++;\r
6804     }\r
6805   }\r
6806   *q = NULLCHAR;\r
6807   strncpy(errorTitle, title, sizeof(errorTitle));\r
6808   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6809   \r
6810   if (modal) {\r
6811     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6812   } else {\r
6813     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6814     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6815                  hwndMain, (DLGPROC)lpProc);\r
6816     FreeProcInstance(lpProc);\r
6817   }\r
6818 }\r
6819 \r
6820 VOID\r
6821 ErrorPopDown()\r
6822 {\r
6823   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6824   if (errorDialog == NULL) return;\r
6825   DestroyWindow(errorDialog);\r
6826   errorDialog = NULL;\r
6827   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6828 }\r
6829 \r
6830 LRESULT CALLBACK\r
6831 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6832 {\r
6833   RECT rChild;\r
6834 \r
6835   switch (message) {\r
6836   case WM_INITDIALOG:\r
6837     GetWindowRect(hDlg, &rChild);\r
6838 \r
6839     /*\r
6840     SetWindowPos(hDlg, NULL, rChild.left,\r
6841       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6842       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6843     */\r
6844 \r
6845     /* \r
6846         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6847         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6848         and it doesn't work when you resize the dialog.\r
6849         For now, just give it a default position.\r
6850     */\r
6851     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6852     Translate(hDlg, DLG_Error);\r
6853 \r
6854     errorDialog = hDlg;\r
6855     SetWindowText(hDlg, errorTitle);\r
6856     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6857     return FALSE;\r
6858 \r
6859   case WM_COMMAND:\r
6860     switch (LOWORD(wParam)) {\r
6861     case IDOK:\r
6862     case IDCANCEL:\r
6863       if (errorDialog == hDlg) errorDialog = NULL;\r
6864       DestroyWindow(hDlg);\r
6865       return TRUE;\r
6866 \r
6867     default:\r
6868       break;\r
6869     }\r
6870     break;\r
6871   }\r
6872   return FALSE;\r
6873 }\r
6874 \r
6875 #ifdef GOTHIC\r
6876 HWND gothicDialog = NULL;\r
6877 \r
6878 LRESULT CALLBACK\r
6879 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6880 {\r
6881   RECT rChild;\r
6882   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6883 \r
6884   switch (message) {\r
6885   case WM_INITDIALOG:\r
6886     GetWindowRect(hDlg, &rChild);\r
6887 \r
6888     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6889                                                              SWP_NOZORDER);\r
6890 \r
6891     /* \r
6892         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6893         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6894         and it doesn't work when you resize the dialog.\r
6895         For now, just give it a default position.\r
6896     */\r
6897     gothicDialog = hDlg;\r
6898     SetWindowText(hDlg, errorTitle);\r
6899     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6900     return FALSE;\r
6901 \r
6902   case WM_COMMAND:\r
6903     switch (LOWORD(wParam)) {\r
6904     case IDOK:\r
6905     case IDCANCEL:\r
6906       if (errorDialog == hDlg) errorDialog = NULL;\r
6907       DestroyWindow(hDlg);\r
6908       return TRUE;\r
6909 \r
6910     default:\r
6911       break;\r
6912     }\r
6913     break;\r
6914   }\r
6915   return FALSE;\r
6916 }\r
6917 \r
6918 VOID\r
6919 GothicPopUp(char *title, VariantClass variant)\r
6920 {\r
6921   FARPROC lpProc;\r
6922   static char *lastTitle;\r
6923 \r
6924   strncpy(errorTitle, title, sizeof(errorTitle));\r
6925   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6926 \r
6927   if(lastTitle != title && gothicDialog != NULL) {\r
6928     DestroyWindow(gothicDialog);\r
6929     gothicDialog = NULL;\r
6930   }\r
6931   if(variant != VariantNormal && gothicDialog == NULL) {\r
6932     title = lastTitle;\r
6933     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6934     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6935                  hwndMain, (DLGPROC)lpProc);\r
6936     FreeProcInstance(lpProc);\r
6937   }\r
6938 }\r
6939 #endif\r
6940 \r
6941 /*---------------------------------------------------------------------------*\\r
6942  *\r
6943  *  Ics Interaction console functions\r
6944  *\r
6945 \*---------------------------------------------------------------------------*/\r
6946 \r
6947 #define HISTORY_SIZE 64\r
6948 static char *history[HISTORY_SIZE];\r
6949 int histIn = 0, histP = 0;\r
6950 \r
6951 \r
6952 VOID\r
6953 SaveInHistory(char *cmd)\r
6954 {\r
6955   if (history[histIn] != NULL) {\r
6956     free(history[histIn]);\r
6957     history[histIn] = NULL;\r
6958   }\r
6959   if (*cmd == NULLCHAR) return;\r
6960   history[histIn] = StrSave(cmd);\r
6961   histIn = (histIn + 1) % HISTORY_SIZE;\r
6962   if (history[histIn] != NULL) {\r
6963     free(history[histIn]);\r
6964 \r
6965     history[histIn] = NULL;\r
6966   }\r
6967   histP = histIn;\r
6968 }\r
6969 \r
6970 char *\r
6971 PrevInHistory(char *cmd)\r
6972 {\r
6973   int newhp;\r
6974   if (histP == histIn) {\r
6975     if (history[histIn] != NULL) free(history[histIn]);\r
6976     history[histIn] = StrSave(cmd);\r
6977   }\r
6978   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6979   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6980   histP = newhp;\r
6981   return history[histP];\r
6982 }\r
6983 \r
6984 char *\r
6985 NextInHistory()\r
6986 {\r
6987   if (histP == histIn) return NULL;\r
6988   histP = (histP + 1) % HISTORY_SIZE;\r
6989   return history[histP];   \r
6990 }\r
6991 \r
6992 HMENU\r
6993 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6994 {\r
6995   HMENU hmenu, h;\r
6996   int i = 0;\r
6997   hmenu = LoadMenu(hInst, "TextMenu");\r
6998   h = GetSubMenu(hmenu, 0);\r
6999   while (e->item) {\r
7000     if (strcmp(e->item, "-") == 0) {\r
7001       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7002     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7003       int flags = MF_STRING, j = 0;\r
7004       if (e->item[0] == '|') {\r
7005         flags |= MF_MENUBARBREAK;\r
7006         j++;\r
7007       }\r
7008       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7009       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7010     }\r
7011     e++;\r
7012     i++;\r
7013   } \r
7014   return hmenu;\r
7015 }\r
7016 \r
7017 WNDPROC consoleTextWindowProc;\r
7018 \r
7019 void\r
7020 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7021 {\r
7022   char buf[MSG_SIZ], name[MSG_SIZ];\r
7023   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7024   CHARRANGE sel;\r
7025 \r
7026   if (!getname) {\r
7027     SetWindowText(hInput, command);\r
7028     if (immediate) {\r
7029       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7030     } else {\r
7031       sel.cpMin = 999999;\r
7032       sel.cpMax = 999999;\r
7033       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7034       SetFocus(hInput);\r
7035     }\r
7036     return;\r
7037   }    \r
7038   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7039   if (sel.cpMin == sel.cpMax) {\r
7040     /* Expand to surrounding word */\r
7041     TEXTRANGE tr;\r
7042     do {\r
7043       tr.chrg.cpMax = sel.cpMin;\r
7044       tr.chrg.cpMin = --sel.cpMin;\r
7045       if (sel.cpMin < 0) break;\r
7046       tr.lpstrText = name;\r
7047       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7048     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7049     sel.cpMin++;\r
7050 \r
7051     do {\r
7052       tr.chrg.cpMin = sel.cpMax;\r
7053       tr.chrg.cpMax = ++sel.cpMax;\r
7054       tr.lpstrText = name;\r
7055       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7056     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7057     sel.cpMax--;\r
7058 \r
7059     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7060       MessageBeep(MB_ICONEXCLAMATION);\r
7061       return;\r
7062     }\r
7063     tr.chrg = sel;\r
7064     tr.lpstrText = name;\r
7065     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7066   } else {\r
7067     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7068       MessageBeep(MB_ICONEXCLAMATION);\r
7069       return;\r
7070     }\r
7071     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7072   }\r
7073   if (immediate) {\r
7074     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7075     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7076     SetWindowText(hInput, buf);\r
7077     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7078   } else {\r
7079     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7080       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7081     SetWindowText(hInput, buf);\r
7082     sel.cpMin = 999999;\r
7083     sel.cpMax = 999999;\r
7084     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7085     SetFocus(hInput);\r
7086   }\r
7087 }\r
7088 \r
7089 LRESULT CALLBACK \r
7090 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7091 {\r
7092   HWND hInput;\r
7093   CHARRANGE sel;\r
7094 \r
7095   switch (message) {\r
7096   case WM_KEYDOWN:\r
7097     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7098     if(wParam=='R') return 0;\r
7099     switch (wParam) {\r
7100     case VK_PRIOR:\r
7101       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7102       return 0;\r
7103     case VK_NEXT:\r
7104       sel.cpMin = 999999;\r
7105       sel.cpMax = 999999;\r
7106       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7107       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7108       return 0;\r
7109     }\r
7110     break;\r
7111   case WM_CHAR:\r
7112    if(wParam != '\022') {\r
7113     if (wParam == '\t') {\r
7114       if (GetKeyState(VK_SHIFT) < 0) {\r
7115         /* shifted */\r
7116         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7117         if (buttonDesc[0].hwnd) {\r
7118           SetFocus(buttonDesc[0].hwnd);\r
7119         } else {\r
7120           SetFocus(hwndMain);\r
7121         }\r
7122       } else {\r
7123         /* unshifted */\r
7124         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7125       }\r
7126     } else {\r
7127       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7128       JAWS_DELETE( SetFocus(hInput); )\r
7129       SendMessage(hInput, message, wParam, lParam);\r
7130     }\r
7131     return 0;\r
7132    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7133    lParam = -1;\r
7134   case WM_RBUTTONDOWN:\r
7135     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7136       /* Move selection here if it was empty */\r
7137       POINT pt;\r
7138       pt.x = LOWORD(lParam);\r
7139       pt.y = HIWORD(lParam);\r
7140       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7141       if (sel.cpMin == sel.cpMax) {\r
7142         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7143         sel.cpMax = sel.cpMin;\r
7144         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7145       }\r
7146       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7147 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7148       POINT pt;\r
7149       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7150       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7151       if (sel.cpMin == sel.cpMax) {\r
7152         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7153         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7154       }\r
7155       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7156         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7157       }\r
7158       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7159       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7160       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7161       MenuPopup(hwnd, pt, hmenu, -1);\r
7162 }\r
7163     }\r
7164     return 0;\r
7165   case WM_RBUTTONUP:\r
7166     if (GetKeyState(VK_SHIFT) & ~1) {\r
7167       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7168         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7169     }\r
7170     return 0;\r
7171   case WM_PASTE:\r
7172     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7173     SetFocus(hInput);\r
7174     return SendMessage(hInput, message, wParam, lParam);\r
7175   case WM_MBUTTONDOWN:\r
7176     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7177   case WM_COMMAND:\r
7178     switch (LOWORD(wParam)) {\r
7179     case IDM_QuickPaste:\r
7180       {\r
7181         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7182         if (sel.cpMin == sel.cpMax) {\r
7183           MessageBeep(MB_ICONEXCLAMATION);\r
7184           return 0;\r
7185         }\r
7186         SendMessage(hwnd, WM_COPY, 0, 0);\r
7187         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7188         SendMessage(hInput, WM_PASTE, 0, 0);\r
7189         SetFocus(hInput);\r
7190         return 0;\r
7191       }\r
7192     case IDM_Cut:\r
7193       SendMessage(hwnd, WM_CUT, 0, 0);\r
7194       return 0;\r
7195     case IDM_Paste:\r
7196       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7197       return 0;\r
7198     case IDM_Copy:\r
7199       SendMessage(hwnd, WM_COPY, 0, 0);\r
7200       return 0;\r
7201     default:\r
7202       {\r
7203         int i = LOWORD(wParam) - IDM_CommandX;\r
7204         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7205             icsTextMenuEntry[i].command != NULL) {\r
7206           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7207                    icsTextMenuEntry[i].getname,\r
7208                    icsTextMenuEntry[i].immediate);\r
7209           return 0;\r
7210         }\r
7211       }\r
7212       break;\r
7213     }\r
7214     break;\r
7215   }\r
7216   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7217 }\r
7218 \r
7219 WNDPROC consoleInputWindowProc;\r
7220 \r
7221 LRESULT CALLBACK\r
7222 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7223 {\r
7224   char buf[MSG_SIZ];\r
7225   char *p;\r
7226   static BOOL sendNextChar = FALSE;\r
7227   static BOOL quoteNextChar = FALSE;\r
7228   InputSource *is = consoleInputSource;\r
7229   CHARFORMAT cf;\r
7230   CHARRANGE sel;\r
7231 \r
7232   switch (message) {\r
7233   case WM_CHAR:\r
7234     if (!appData.localLineEditing || sendNextChar) {\r
7235       is->buf[0] = (CHAR) wParam;\r
7236       is->count = 1;\r
7237       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7238       sendNextChar = FALSE;\r
7239       return 0;\r
7240     }\r
7241     if (quoteNextChar) {\r
7242       buf[0] = (char) wParam;\r
7243       buf[1] = NULLCHAR;\r
7244       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7245       quoteNextChar = FALSE;\r
7246       return 0;\r
7247     }\r
7248     switch (wParam) {\r
7249     case '\r':   /* Enter key */\r
7250       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7251       if (consoleEcho) SaveInHistory(is->buf);\r
7252       is->buf[is->count++] = '\n';\r
7253       is->buf[is->count] = NULLCHAR;\r
7254       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7255       if (consoleEcho) {\r
7256         ConsoleOutput(is->buf, is->count, TRUE);\r
7257       } else if (appData.localLineEditing) {\r
7258         ConsoleOutput("\n", 1, TRUE);\r
7259       }\r
7260       /* fall thru */\r
7261     case '\033': /* Escape key */\r
7262       SetWindowText(hwnd, "");\r
7263       cf.cbSize = sizeof(CHARFORMAT);\r
7264       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7265       if (consoleEcho) {\r
7266         cf.crTextColor = textAttribs[ColorNormal].color;\r
7267       } else {\r
7268         cf.crTextColor = COLOR_ECHOOFF;\r
7269       }\r
7270       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7271       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7272       return 0;\r
7273     case '\t':   /* Tab key */\r
7274       if (GetKeyState(VK_SHIFT) < 0) {\r
7275         /* shifted */\r
7276         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7277       } else {\r
7278         /* unshifted */\r
7279         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7280         if (buttonDesc[0].hwnd) {\r
7281           SetFocus(buttonDesc[0].hwnd);\r
7282         } else {\r
7283           SetFocus(hwndMain);\r
7284         }\r
7285       }\r
7286       return 0;\r
7287     case '\023': /* Ctrl+S */\r
7288       sendNextChar = TRUE;\r
7289       return 0;\r
7290     case '\021': /* Ctrl+Q */\r
7291       quoteNextChar = TRUE;\r
7292       return 0;\r
7293     JAWS_REPLAY\r
7294     default:\r
7295       break;\r
7296     }\r
7297     break;\r
7298   case WM_KEYDOWN:\r
7299     switch (wParam) {\r
7300     case VK_UP:\r
7301       GetWindowText(hwnd, buf, MSG_SIZ);\r
7302       p = PrevInHistory(buf);\r
7303       if (p != NULL) {\r
7304         SetWindowText(hwnd, p);\r
7305         sel.cpMin = 999999;\r
7306         sel.cpMax = 999999;\r
7307         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7308         return 0;\r
7309       }\r
7310       break;\r
7311     case VK_DOWN:\r
7312       p = NextInHistory();\r
7313       if (p != NULL) {\r
7314         SetWindowText(hwnd, p);\r
7315         sel.cpMin = 999999;\r
7316         sel.cpMax = 999999;\r
7317         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7318         return 0;\r
7319       }\r
7320       break;\r
7321     case VK_HOME:\r
7322     case VK_END:\r
7323       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7324       /* fall thru */\r
7325     case VK_PRIOR:\r
7326     case VK_NEXT:\r
7327       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7328       return 0;\r
7329     }\r
7330     break;\r
7331   case WM_MBUTTONDOWN:\r
7332     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7333       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7334     break;\r
7335   case WM_RBUTTONUP:\r
7336     if (GetKeyState(VK_SHIFT) & ~1) {\r
7337       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7338         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7339     } else {\r
7340       POINT pt;\r
7341       HMENU hmenu;\r
7342       hmenu = LoadMenu(hInst, "InputMenu");\r
7343       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7344       if (sel.cpMin == sel.cpMax) {\r
7345         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7346         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7347       }\r
7348       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7349         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7350       }\r
7351       pt.x = LOWORD(lParam);\r
7352       pt.y = HIWORD(lParam);\r
7353       MenuPopup(hwnd, pt, hmenu, -1);\r
7354     }\r
7355     return 0;\r
7356   case WM_COMMAND:\r
7357     switch (LOWORD(wParam)) { \r
7358     case IDM_Undo:\r
7359       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7360       return 0;\r
7361     case IDM_SelectAll:\r
7362       sel.cpMin = 0;\r
7363       sel.cpMax = -1; /*999999?*/\r
7364       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7365       return 0;\r
7366     case IDM_Cut:\r
7367       SendMessage(hwnd, WM_CUT, 0, 0);\r
7368       return 0;\r
7369     case IDM_Paste:\r
7370       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7371       return 0;\r
7372     case IDM_Copy:\r
7373       SendMessage(hwnd, WM_COPY, 0, 0);\r
7374       return 0;\r
7375     }\r
7376     break;\r
7377   }\r
7378   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7379 }\r
7380 \r
7381 #define CO_MAX  100000\r
7382 #define CO_TRIM   1000\r
7383 \r
7384 LRESULT CALLBACK\r
7385 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7386 {\r
7387   static SnapData sd;\r
7388   HWND hText, hInput;\r
7389   RECT rect;\r
7390   static int sizeX, sizeY;\r
7391   int newSizeX, newSizeY;\r
7392   MINMAXINFO *mmi;\r
7393   WORD wMask;\r
7394 \r
7395   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7396   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7397 \r
7398   switch (message) {\r
7399   case WM_NOTIFY:\r
7400     if (((NMHDR*)lParam)->code == EN_LINK)\r
7401     {\r
7402       ENLINK *pLink = (ENLINK*)lParam;\r
7403       if (pLink->msg == WM_LBUTTONUP)\r
7404       {\r
7405         TEXTRANGE tr;\r
7406 \r
7407         tr.chrg = pLink->chrg;\r
7408         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7409         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7410         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7411         free(tr.lpstrText);\r
7412       }\r
7413     }\r
7414     break;\r
7415   case WM_INITDIALOG: /* message: initialize dialog box */\r
7416     hwndConsole = hDlg;\r
7417     SetFocus(hInput);\r
7418     consoleTextWindowProc = (WNDPROC)\r
7419       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7420     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7421     consoleInputWindowProc = (WNDPROC)\r
7422       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7423     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7424     Colorize(ColorNormal, TRUE);\r
7425     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7426     ChangedConsoleFont();\r
7427     GetClientRect(hDlg, &rect);\r
7428     sizeX = rect.right;\r
7429     sizeY = rect.bottom;\r
7430     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7431         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7432       WINDOWPLACEMENT wp;\r
7433       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7434       wp.length = sizeof(WINDOWPLACEMENT);\r
7435       wp.flags = 0;\r
7436       wp.showCmd = SW_SHOW;\r
7437       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7438       wp.rcNormalPosition.left = wpConsole.x;\r
7439       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7440       wp.rcNormalPosition.top = wpConsole.y;\r
7441       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7442       SetWindowPlacement(hDlg, &wp);\r
7443     }\r
7444 \r
7445    // [HGM] Chessknight's change 2004-07-13\r
7446    else { /* Determine Defaults */\r
7447        WINDOWPLACEMENT wp;\r
7448        wpConsole.x = wpMain.width + 1;\r
7449        wpConsole.y = wpMain.y;\r
7450        wpConsole.width = screenWidth -  wpMain.width;\r
7451        wpConsole.height = wpMain.height;\r
7452        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7453        wp.length = sizeof(WINDOWPLACEMENT);\r
7454        wp.flags = 0;\r
7455        wp.showCmd = SW_SHOW;\r
7456        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7457        wp.rcNormalPosition.left = wpConsole.x;\r
7458        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7459        wp.rcNormalPosition.top = wpConsole.y;\r
7460        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7461        SetWindowPlacement(hDlg, &wp);\r
7462     }\r
7463 \r
7464    // Allow hText to highlight URLs and send notifications on them\r
7465    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7466    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7467    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7468    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7469 \r
7470     return FALSE;\r
7471 \r
7472   case WM_SETFOCUS:\r
7473     SetFocus(hInput);\r
7474     return 0;\r
7475 \r
7476   case WM_CLOSE:\r
7477     ExitEvent(0);\r
7478     /* not reached */\r
7479     break;\r
7480 \r
7481   case WM_SIZE:\r
7482     if (IsIconic(hDlg)) break;\r
7483     newSizeX = LOWORD(lParam);\r
7484     newSizeY = HIWORD(lParam);\r
7485     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7486       RECT rectText, rectInput;\r
7487       POINT pt;\r
7488       int newTextHeight, newTextWidth;\r
7489       GetWindowRect(hText, &rectText);\r
7490       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7491       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7492       if (newTextHeight < 0) {\r
7493         newSizeY += -newTextHeight;\r
7494         newTextHeight = 0;\r
7495       }\r
7496       SetWindowPos(hText, NULL, 0, 0,\r
7497         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7498       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7499       pt.x = rectInput.left;\r
7500       pt.y = rectInput.top + newSizeY - sizeY;\r
7501       ScreenToClient(hDlg, &pt);\r
7502       SetWindowPos(hInput, NULL, \r
7503         pt.x, pt.y, /* needs client coords */   \r
7504         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7505         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7506     }\r
7507     sizeX = newSizeX;\r
7508     sizeY = newSizeY;\r
7509     break;\r
7510 \r
7511   case WM_GETMINMAXINFO:\r
7512     /* Prevent resizing window too small */\r
7513     mmi = (MINMAXINFO *) lParam;\r
7514     mmi->ptMinTrackSize.x = 100;\r
7515     mmi->ptMinTrackSize.y = 100;\r
7516     break;\r
7517 \r
7518   /* [AS] Snapping */\r
7519   case WM_ENTERSIZEMOVE:\r
7520     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7521 \r
7522   case WM_SIZING:\r
7523     return OnSizing( &sd, hDlg, wParam, lParam );\r
7524 \r
7525   case WM_MOVING:\r
7526     return OnMoving( &sd, hDlg, wParam, lParam );\r
7527 \r
7528   case WM_EXITSIZEMOVE:\r
7529         UpdateICSWidth(hText);\r
7530     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7531   }\r
7532 \r
7533   return DefWindowProc(hDlg, message, wParam, lParam);\r
7534 }\r
7535 \r
7536 \r
7537 VOID\r
7538 ConsoleCreate()\r
7539 {\r
7540   HWND hCons;\r
7541   if (hwndConsole) return;\r
7542   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7543   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7544 }\r
7545 \r
7546 \r
7547 VOID\r
7548 ConsoleOutput(char* data, int length, int forceVisible)\r
7549 {\r
7550   HWND hText;\r
7551   int trim, exlen;\r
7552   char *p, *q;\r
7553   char buf[CO_MAX+1];\r
7554   POINT pEnd;\r
7555   RECT rect;\r
7556   static int delayLF = 0;\r
7557   CHARRANGE savesel, sel;\r
7558 \r
7559   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7560   p = data;\r
7561   q = buf;\r
7562   if (delayLF) {\r
7563     *q++ = '\r';\r
7564     *q++ = '\n';\r
7565     delayLF = 0;\r
7566   }\r
7567   while (length--) {\r
7568     if (*p == '\n') {\r
7569       if (*++p) {\r
7570         *q++ = '\r';\r
7571         *q++ = '\n';\r
7572       } else {\r
7573         delayLF = 1;\r
7574       }\r
7575     } else if (*p == '\007') {\r
7576        MyPlaySound(&sounds[(int)SoundBell]);\r
7577        p++;\r
7578     } else {\r
7579       *q++ = *p++;\r
7580     }\r
7581   }\r
7582   *q = NULLCHAR;\r
7583   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7584   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7585   /* Save current selection */\r
7586   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7587   exlen = GetWindowTextLength(hText);\r
7588   /* Find out whether current end of text is visible */\r
7589   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7590   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7591   /* Trim existing text if it's too long */\r
7592   if (exlen + (q - buf) > CO_MAX) {\r
7593     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7594     sel.cpMin = 0;\r
7595     sel.cpMax = trim;\r
7596     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7597     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7598     exlen -= trim;\r
7599     savesel.cpMin -= trim;\r
7600     savesel.cpMax -= trim;\r
7601     if (exlen < 0) exlen = 0;\r
7602     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7603     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7604   }\r
7605   /* Append the new text */\r
7606   sel.cpMin = exlen;\r
7607   sel.cpMax = exlen;\r
7608   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7609   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7610   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7611   if (forceVisible || exlen == 0 ||\r
7612       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7613        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7614     /* Scroll to make new end of text visible if old end of text\r
7615        was visible or new text is an echo of user typein */\r
7616     sel.cpMin = 9999999;\r
7617     sel.cpMax = 9999999;\r
7618     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7619     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7620     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7621     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7622   }\r
7623   if (savesel.cpMax == exlen || forceVisible) {\r
7624     /* Move insert point to new end of text if it was at the old\r
7625        end of text or if the new text is an echo of user typein */\r
7626     sel.cpMin = 9999999;\r
7627     sel.cpMax = 9999999;\r
7628     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7629   } else {\r
7630     /* Restore previous selection */\r
7631     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7632   }\r
7633   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7634 }\r
7635 \r
7636 /*---------*/\r
7637 \r
7638 \r
7639 void\r
7640 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7641 {\r
7642   char buf[100];\r
7643   char *str;\r
7644   COLORREF oldFg, oldBg;\r
7645   HFONT oldFont;\r
7646   RECT rect;\r
7647 \r
7648   if(copyNumber > 1)\r
7649     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7650 \r
7651   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7652   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7653   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7654 \r
7655   rect.left = x;\r
7656   rect.right = x + squareSize;\r
7657   rect.top  = y;\r
7658   rect.bottom = y + squareSize;\r
7659   str = buf;\r
7660 \r
7661   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7662                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7663              y, ETO_CLIPPED|ETO_OPAQUE,\r
7664              &rect, str, strlen(str), NULL);\r
7665 \r
7666   (void) SetTextColor(hdc, oldFg);\r
7667   (void) SetBkColor(hdc, oldBg);\r
7668   (void) SelectObject(hdc, oldFont);\r
7669 }\r
7670 \r
7671 void\r
7672 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7673               RECT *rect, char *color, char *flagFell)\r
7674 {\r
7675   char buf[100];\r
7676   char *str;\r
7677   COLORREF oldFg, oldBg;\r
7678   HFONT oldFont;\r
7679 \r
7680   if (twoBoards && partnerUp) return;\r
7681   if (appData.clockMode) {\r
7682     if (tinyLayout == 2)\r
7683       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7684     else\r
7685       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7686     str = buf;\r
7687   } else {\r
7688     str = color;\r
7689   }\r
7690 \r
7691   if (highlight) {\r
7692     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7693     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7694   } else {\r
7695     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7696     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7697   }\r
7698 \r
7699   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7700 \r
7701   JAWS_SILENCE\r
7702 \r
7703   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7704              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7705              rect, str, strlen(str), NULL);\r
7706   if(logoHeight > 0 && appData.clockMode) {\r
7707       RECT r;\r
7708       str += strlen(color)+2;\r
7709       r.top = rect->top + logoHeight/2;\r
7710       r.left = rect->left;\r
7711       r.right = rect->right;\r
7712       r.bottom = rect->bottom;\r
7713       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7714                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7715                  &r, str, strlen(str), NULL);\r
7716   }\r
7717   (void) SetTextColor(hdc, oldFg);\r
7718   (void) SetBkColor(hdc, oldBg);\r
7719   (void) SelectObject(hdc, oldFont);\r
7720 }\r
7721 \r
7722 \r
7723 int\r
7724 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7725            OVERLAPPED *ovl)\r
7726 {\r
7727   int ok, err;\r
7728 \r
7729   /* [AS]  */\r
7730   if( count <= 0 ) {\r
7731     if (appData.debugMode) {\r
7732       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7733     }\r
7734 \r
7735     return ERROR_INVALID_USER_BUFFER;\r
7736   }\r
7737 \r
7738   ResetEvent(ovl->hEvent);\r
7739   ovl->Offset = ovl->OffsetHigh = 0;\r
7740   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7741   if (ok) {\r
7742     err = NO_ERROR;\r
7743   } else {\r
7744     err = GetLastError();\r
7745     if (err == ERROR_IO_PENDING) {\r
7746       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7747       if (ok)\r
7748         err = NO_ERROR;\r
7749       else\r
7750         err = GetLastError();\r
7751     }\r
7752   }\r
7753   return err;\r
7754 }\r
7755 \r
7756 int\r
7757 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7758             OVERLAPPED *ovl)\r
7759 {\r
7760   int ok, err;\r
7761 \r
7762   ResetEvent(ovl->hEvent);\r
7763   ovl->Offset = ovl->OffsetHigh = 0;\r
7764   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7765   if (ok) {\r
7766     err = NO_ERROR;\r
7767   } else {\r
7768     err = GetLastError();\r
7769     if (err == ERROR_IO_PENDING) {\r
7770       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7771       if (ok)\r
7772         err = NO_ERROR;\r
7773       else\r
7774         err = GetLastError();\r
7775     }\r
7776 \r
7777   }\r
7778   return err;\r
7779 }\r
7780 \r
7781 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7782 void CheckForInputBufferFull( InputSource * is )\r
7783 {\r
7784     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7785         /* Look for end of line */\r
7786         char * p = is->buf;\r
7787         \r
7788         while( p < is->next && *p != '\n' ) {\r
7789             p++;\r
7790         }\r
7791 \r
7792         if( p >= is->next ) {\r
7793             if (appData.debugMode) {\r
7794                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7795             }\r
7796 \r
7797             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7798             is->count = (DWORD) -1;\r
7799             is->next = is->buf;\r
7800         }\r
7801     }\r
7802 }\r
7803 \r
7804 DWORD\r
7805 InputThread(LPVOID arg)\r
7806 {\r
7807   InputSource *is;\r
7808   OVERLAPPED ovl;\r
7809 \r
7810   is = (InputSource *) arg;\r
7811   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7812   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7813   while (is->hThread != NULL) {\r
7814     is->error = DoReadFile(is->hFile, is->next,\r
7815                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7816                            &is->count, &ovl);\r
7817     if (is->error == NO_ERROR) {\r
7818       is->next += is->count;\r
7819     } else {\r
7820       if (is->error == ERROR_BROKEN_PIPE) {\r
7821         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7822         is->count = 0;\r
7823       } else {\r
7824         is->count = (DWORD) -1;\r
7825         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7826         break; \r
7827       }\r
7828     }\r
7829 \r
7830     CheckForInputBufferFull( is );\r
7831 \r
7832     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7833 \r
7834     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7835 \r
7836     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7837   }\r
7838 \r
7839   CloseHandle(ovl.hEvent);\r
7840   CloseHandle(is->hFile);\r
7841 \r
7842   if (appData.debugMode) {\r
7843     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7844   }\r
7845 \r
7846   return 0;\r
7847 }\r
7848 \r
7849 \r
7850 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7851 DWORD\r
7852 NonOvlInputThread(LPVOID arg)\r
7853 {\r
7854   InputSource *is;\r
7855   char *p, *q;\r
7856   int i;\r
7857   char prev;\r
7858 \r
7859   is = (InputSource *) arg;\r
7860   while (is->hThread != NULL) {\r
7861     is->error = ReadFile(is->hFile, is->next,\r
7862                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7863                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7864     if (is->error == NO_ERROR) {\r
7865       /* Change CRLF to LF */\r
7866       if (is->next > is->buf) {\r
7867         p = is->next - 1;\r
7868         i = is->count + 1;\r
7869       } else {\r
7870         p = is->next;\r
7871         i = is->count;\r
7872       }\r
7873       q = p;\r
7874       prev = NULLCHAR;\r
7875       while (i > 0) {\r
7876         if (prev == '\r' && *p == '\n') {\r
7877           *(q-1) = '\n';\r
7878           is->count--;\r
7879         } else { \r
7880           *q++ = *p;\r
7881         }\r
7882         prev = *p++;\r
7883         i--;\r
7884       }\r
7885       *q = NULLCHAR;\r
7886       is->next = q;\r
7887     } else {\r
7888       if (is->error == ERROR_BROKEN_PIPE) {\r
7889         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7890         is->count = 0; \r
7891       } else {\r
7892         is->count = (DWORD) -1;\r
7893       }\r
7894     }\r
7895 \r
7896     CheckForInputBufferFull( is );\r
7897 \r
7898     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7899 \r
7900     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7901 \r
7902     if (is->count < 0) break;  /* Quit on error */\r
7903   }\r
7904   CloseHandle(is->hFile);\r
7905   return 0;\r
7906 }\r
7907 \r
7908 DWORD\r
7909 SocketInputThread(LPVOID arg)\r
7910 {\r
7911   InputSource *is;\r
7912 \r
7913   is = (InputSource *) arg;\r
7914   while (is->hThread != NULL) {\r
7915     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7916     if ((int)is->count == SOCKET_ERROR) {\r
7917       is->count = (DWORD) -1;\r
7918       is->error = WSAGetLastError();\r
7919     } else {\r
7920       is->error = NO_ERROR;\r
7921       is->next += is->count;\r
7922       if (is->count == 0 && is->second == is) {\r
7923         /* End of file on stderr; quit with no message */\r
7924         break;\r
7925       }\r
7926     }\r
7927     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7928 \r
7929     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7930 \r
7931     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7932   }\r
7933   return 0;\r
7934 }\r
7935 \r
7936 VOID\r
7937 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7938 {\r
7939   InputSource *is;\r
7940 \r
7941   is = (InputSource *) lParam;\r
7942   if (is->lineByLine) {\r
7943     /* Feed in lines one by one */\r
7944     char *p = is->buf;\r
7945     char *q = p;\r
7946     while (q < is->next) {\r
7947       if (*q++ == '\n') {\r
7948         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7949         p = q;\r
7950       }\r
7951     }\r
7952     \r
7953     /* Move any partial line to the start of the buffer */\r
7954     q = is->buf;\r
7955     while (p < is->next) {\r
7956       *q++ = *p++;\r
7957     }\r
7958     is->next = q;\r
7959 \r
7960     if (is->error != NO_ERROR || is->count == 0) {\r
7961       /* Notify backend of the error.  Note: If there was a partial\r
7962          line at the end, it is not flushed through. */\r
7963       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7964     }\r
7965   } else {\r
7966     /* Feed in the whole chunk of input at once */\r
7967     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7968     is->next = is->buf;\r
7969   }\r
7970 }\r
7971 \r
7972 /*---------------------------------------------------------------------------*\\r
7973  *\r
7974  *  Menu enables. Used when setting various modes.\r
7975  *\r
7976 \*---------------------------------------------------------------------------*/\r
7977 \r
7978 typedef struct {\r
7979   int item;\r
7980   int flags;\r
7981 } Enables;\r
7982 \r
7983 VOID\r
7984 GreyRevert(Boolean grey)\r
7985 { // [HGM] vari: for retracting variations in local mode\r
7986   HMENU hmenu = GetMenu(hwndMain);\r
7987   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7988   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7989 }\r
7990 \r
7991 VOID\r
7992 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7993 {\r
7994   while (enab->item > 0) {\r
7995     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7996     enab++;\r
7997   }\r
7998 }\r
7999 \r
8000 Enables gnuEnables[] = {\r
8001   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8014 \r
8015 \r
8016   // Needed to switch from ncp to GNU mode on Engine Load\r
8017   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8018   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8019   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8020   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8021   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8029   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8031   { -1, -1 }\r
8032 };\r
8033 \r
8034 Enables icsEnables[] = {\r
8035   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8055   { -1, -1 }\r
8056 };\r
8057 \r
8058 #if ZIPPY\r
8059 Enables zippyEnables[] = {\r
8060   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8061   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8062   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8063   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8064   { -1, -1 }\r
8065 };\r
8066 #endif\r
8067 \r
8068 Enables ncpEnables[] = {\r
8069   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8076   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8077   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8078   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8079   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8080   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8081   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8082   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8083   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8084   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8085   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8086   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8091   { -1, -1 }\r
8092 };\r
8093 \r
8094 Enables trainingOnEnables[] = {\r
8095   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8104   { -1, -1 }\r
8105 };\r
8106 \r
8107 Enables trainingOffEnables[] = {\r
8108   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8109   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8110   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8111   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8112   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8113   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8115   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8116   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8117   { -1, -1 }\r
8118 };\r
8119 \r
8120 /* These modify either ncpEnables or gnuEnables */\r
8121 Enables cmailEnables[] = {\r
8122   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8124   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8125   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8129   { -1, -1 }\r
8130 };\r
8131 \r
8132 Enables machineThinkingEnables[] = {\r
8133   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8134   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8135   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8136   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8137   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8138   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8139   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8140   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8141   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8142   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8143   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8144   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8145   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8146 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8147   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8148   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8149   { -1, -1 }\r
8150 };\r
8151 \r
8152 Enables userThinkingEnables[] = {\r
8153   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8154   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8155   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8156   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8157   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8158   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8159   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8160   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8161   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8162   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8163   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8164   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8165   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8166 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8167   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8168   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8169   { -1, -1 }\r
8170 };\r
8171 \r
8172 /*---------------------------------------------------------------------------*\\r
8173  *\r
8174  *  Front-end interface functions exported by XBoard.\r
8175  *  Functions appear in same order as prototypes in frontend.h.\r
8176  * \r
8177 \*---------------------------------------------------------------------------*/\r
8178 VOID\r
8179 CheckMark(UINT item, int state)\r
8180 {\r
8181     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8182 }\r
8183 \r
8184 VOID\r
8185 ModeHighlight()\r
8186 {\r
8187   static UINT prevChecked = 0;\r
8188   static int prevPausing = 0;\r
8189   UINT nowChecked;\r
8190 \r
8191   if (pausing != prevPausing) {\r
8192     prevPausing = pausing;\r
8193     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8194                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8195     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8196   }\r
8197 \r
8198   switch (gameMode) {\r
8199   case BeginningOfGame:\r
8200     if (appData.icsActive)\r
8201       nowChecked = IDM_IcsClient;\r
8202     else if (appData.noChessProgram)\r
8203       nowChecked = IDM_EditGame;\r
8204     else\r
8205       nowChecked = IDM_MachineBlack;\r
8206     break;\r
8207   case MachinePlaysBlack:\r
8208     nowChecked = IDM_MachineBlack;\r
8209     break;\r
8210   case MachinePlaysWhite:\r
8211     nowChecked = IDM_MachineWhite;\r
8212     break;\r
8213   case TwoMachinesPlay:\r
8214     nowChecked = IDM_TwoMachines;\r
8215     break;\r
8216   case AnalyzeMode:\r
8217     nowChecked = IDM_AnalysisMode;\r
8218     break;\r
8219   case AnalyzeFile:\r
8220     nowChecked = IDM_AnalyzeFile;\r
8221     break;\r
8222   case EditGame:\r
8223     nowChecked = IDM_EditGame;\r
8224     break;\r
8225   case PlayFromGameFile:\r
8226     nowChecked = IDM_LoadGame;\r
8227     break;\r
8228   case EditPosition:\r
8229     nowChecked = IDM_EditPosition;\r
8230     break;\r
8231   case Training:\r
8232     nowChecked = IDM_Training;\r
8233     break;\r
8234   case IcsPlayingWhite:\r
8235   case IcsPlayingBlack:\r
8236   case IcsObserving:\r
8237   case IcsIdle:\r
8238     nowChecked = IDM_IcsClient;\r
8239     break;\r
8240   default:\r
8241   case EndOfGame:\r
8242     nowChecked = 0;\r
8243     break;\r
8244   }\r
8245   if(prevChecked == IDM_TwoMachine) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8246     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED)\r
8247   CheckMark(prevChecked, MF_UNCHECKED);\r
8248   CheckMark(nowChecked, MF_CHECKED);\r
8249   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8250 \r
8251   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8252     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8253                           MF_BYCOMMAND|MF_ENABLED);\r
8254   } else {\r
8255     (void) EnableMenuItem(GetMenu(hwndMain), \r
8256                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8257   }\r
8258 \r
8259   prevChecked = nowChecked;\r
8260 \r
8261   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8262   if (appData.icsActive) {\r
8263        if (appData.icsEngineAnalyze) {\r
8264                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8265        } else {\r
8266                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8267        }\r
8268   }\r
8269   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8270 }\r
8271 \r
8272 VOID\r
8273 SetICSMode()\r
8274 {\r
8275   HMENU hmenu = GetMenu(hwndMain);\r
8276   SetMenuEnables(hmenu, icsEnables);\r
8277   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8278     MF_BYCOMMAND|MF_ENABLED);\r
8279 #if ZIPPY\r
8280   if (appData.zippyPlay) {\r
8281     SetMenuEnables(hmenu, zippyEnables);\r
8282     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8283          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8284           MF_BYCOMMAND|MF_ENABLED);\r
8285   }\r
8286 #endif\r
8287 }\r
8288 \r
8289 VOID\r
8290 SetGNUMode()\r
8291 {\r
8292   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8293 }\r
8294 \r
8295 VOID\r
8296 SetNCPMode()\r
8297 {\r
8298   HMENU hmenu = GetMenu(hwndMain);\r
8299   SetMenuEnables(hmenu, ncpEnables);\r
8300     DrawMenuBar(hwndMain);\r
8301 }\r
8302 \r
8303 VOID\r
8304 SetCmailMode()\r
8305 {\r
8306   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8307 }\r
8308 \r
8309 VOID \r
8310 SetTrainingModeOn()\r
8311 {\r
8312   int i;\r
8313   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8314   for (i = 0; i < N_BUTTONS; i++) {\r
8315     if (buttonDesc[i].hwnd != NULL)\r
8316       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8317   }\r
8318   CommentPopDown();\r
8319 }\r
8320 \r
8321 VOID SetTrainingModeOff()\r
8322 {\r
8323   int i;\r
8324   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8325   for (i = 0; i < N_BUTTONS; i++) {\r
8326     if (buttonDesc[i].hwnd != NULL)\r
8327       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8328   }\r
8329 }\r
8330 \r
8331 \r
8332 VOID\r
8333 SetUserThinkingEnables()\r
8334 {\r
8335   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8336 }\r
8337 \r
8338 VOID\r
8339 SetMachineThinkingEnables()\r
8340 {\r
8341   HMENU hMenu = GetMenu(hwndMain);\r
8342   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8343 \r
8344   SetMenuEnables(hMenu, machineThinkingEnables);\r
8345 \r
8346   if (gameMode == MachinePlaysBlack) {\r
8347     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8348   } else if (gameMode == MachinePlaysWhite) {\r
8349     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8350   } else if (gameMode == TwoMachinesPlay) {\r
8351     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8352   }\r
8353 }\r
8354 \r
8355 \r
8356 VOID\r
8357 DisplayTitle(char *str)\r
8358 {\r
8359   char title[MSG_SIZ], *host;\r
8360   if (str[0] != NULLCHAR) {\r
8361     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8362   } else if (appData.icsActive) {\r
8363     if (appData.icsCommPort[0] != NULLCHAR)\r
8364       host = "ICS";\r
8365     else \r
8366       host = appData.icsHost;\r
8367       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8368   } else if (appData.noChessProgram) {\r
8369     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8370   } else {\r
8371     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8372     strcat(title, ": ");\r
8373     strcat(title, first.tidy);\r
8374   }\r
8375   SetWindowText(hwndMain, title);\r
8376 }\r
8377 \r
8378 \r
8379 VOID\r
8380 DisplayMessage(char *str1, char *str2)\r
8381 {\r
8382   HDC hdc;\r
8383   HFONT oldFont;\r
8384   int remain = MESSAGE_TEXT_MAX - 1;\r
8385   int len;\r
8386 \r
8387   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8388   messageText[0] = NULLCHAR;\r
8389   if (*str1) {\r
8390     len = strlen(str1);\r
8391     if (len > remain) len = remain;\r
8392     strncpy(messageText, str1, len);\r
8393     messageText[len] = NULLCHAR;\r
8394     remain -= len;\r
8395   }\r
8396   if (*str2 && remain >= 2) {\r
8397     if (*str1) {\r
8398       strcat(messageText, "  ");\r
8399       remain -= 2;\r
8400     }\r
8401     len = strlen(str2);\r
8402     if (len > remain) len = remain;\r
8403     strncat(messageText, str2, len);\r
8404   }\r
8405   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8406   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8407 \r
8408   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8409 \r
8410   SAYMACHINEMOVE();\r
8411 \r
8412   hdc = GetDC(hwndMain);\r
8413   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8414   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8415              &messageRect, messageText, strlen(messageText), NULL);\r
8416   (void) SelectObject(hdc, oldFont);\r
8417   (void) ReleaseDC(hwndMain, hdc);\r
8418 }\r
8419 \r
8420 VOID\r
8421 DisplayError(char *str, int error)\r
8422 {\r
8423   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8424   int len;\r
8425 \r
8426   if (error == 0) {\r
8427     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8428   } else {\r
8429     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8430                         NULL, error, LANG_NEUTRAL,\r
8431                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8432     if (len > 0) {\r
8433       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8434     } else {\r
8435       ErrorMap *em = errmap;\r
8436       while (em->err != 0 && em->err != error) em++;\r
8437       if (em->err != 0) {\r
8438         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8439       } else {\r
8440         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8441       }\r
8442     }\r
8443   }\r
8444   \r
8445   ErrorPopUp(_("Error"), buf);\r
8446 }\r
8447 \r
8448 \r
8449 VOID\r
8450 DisplayMoveError(char *str)\r
8451 {\r
8452   fromX = fromY = -1;\r
8453   ClearHighlights();\r
8454   DrawPosition(FALSE, NULL);\r
8455   if (appData.popupMoveErrors) {\r
8456     ErrorPopUp(_("Error"), str);\r
8457   } else {\r
8458     DisplayMessage(str, "");\r
8459     moveErrorMessageUp = TRUE;\r
8460   }\r
8461 }\r
8462 \r
8463 VOID\r
8464 DisplayFatalError(char *str, int error, int exitStatus)\r
8465 {\r
8466   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8467   int len;\r
8468   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8469 \r
8470   if (error != 0) {\r
8471     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8472                         NULL, error, LANG_NEUTRAL,\r
8473                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8474     if (len > 0) {\r
8475       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8476     } else {\r
8477       ErrorMap *em = errmap;\r
8478       while (em->err != 0 && em->err != error) em++;\r
8479       if (em->err != 0) {\r
8480         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8481       } else {\r
8482         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8483       }\r
8484     }\r
8485     str = buf;\r
8486   }\r
8487   if (appData.debugMode) {\r
8488     fprintf(debugFP, "%s: %s\n", label, str);\r
8489   }\r
8490   if (appData.popupExitMessage) {\r
8491     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8492                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8493   }\r
8494   ExitEvent(exitStatus);\r
8495 }\r
8496 \r
8497 \r
8498 VOID\r
8499 DisplayInformation(char *str)\r
8500 {\r
8501   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8502 }\r
8503 \r
8504 \r
8505 VOID\r
8506 DisplayNote(char *str)\r
8507 {\r
8508   ErrorPopUp(_("Note"), str);\r
8509 }\r
8510 \r
8511 \r
8512 typedef struct {\r
8513   char *title, *question, *replyPrefix;\r
8514   ProcRef pr;\r
8515 } QuestionParams;\r
8516 \r
8517 LRESULT CALLBACK\r
8518 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8519 {\r
8520   static QuestionParams *qp;\r
8521   char reply[MSG_SIZ];\r
8522   int len, err;\r
8523 \r
8524   switch (message) {\r
8525   case WM_INITDIALOG:\r
8526     qp = (QuestionParams *) lParam;\r
8527     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8528     Translate(hDlg, DLG_Question);\r
8529     SetWindowText(hDlg, qp->title);\r
8530     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8531     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8532     return FALSE;\r
8533 \r
8534   case WM_COMMAND:\r
8535     switch (LOWORD(wParam)) {\r
8536     case IDOK:\r
8537       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8538       if (*reply) strcat(reply, " ");\r
8539       len = strlen(reply);\r
8540       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8541       strcat(reply, "\n");\r
8542       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8543       EndDialog(hDlg, TRUE);\r
8544       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8545       return TRUE;\r
8546     case IDCANCEL:\r
8547       EndDialog(hDlg, FALSE);\r
8548       return TRUE;\r
8549     default:\r
8550       break;\r
8551     }\r
8552     break;\r
8553   }\r
8554   return FALSE;\r
8555 }\r
8556 \r
8557 VOID\r
8558 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8559 {\r
8560     QuestionParams qp;\r
8561     FARPROC lpProc;\r
8562     \r
8563     qp.title = title;\r
8564     qp.question = question;\r
8565     qp.replyPrefix = replyPrefix;\r
8566     qp.pr = pr;\r
8567     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8568     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8569       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8570     FreeProcInstance(lpProc);\r
8571 }\r
8572 \r
8573 /* [AS] Pick FRC position */\r
8574 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8575 {\r
8576     static int * lpIndexFRC;\r
8577     BOOL index_is_ok;\r
8578     char buf[16];\r
8579 \r
8580     switch( message )\r
8581     {\r
8582     case WM_INITDIALOG:\r
8583         lpIndexFRC = (int *) lParam;\r
8584 \r
8585         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8586         Translate(hDlg, DLG_NewGameFRC);\r
8587 \r
8588         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8589         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8590         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8591         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8592 \r
8593         break;\r
8594 \r
8595     case WM_COMMAND:\r
8596         switch( LOWORD(wParam) ) {\r
8597         case IDOK:\r
8598             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8599             EndDialog( hDlg, 0 );\r
8600             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8601             return TRUE;\r
8602         case IDCANCEL:\r
8603             EndDialog( hDlg, 1 );   \r
8604             return TRUE;\r
8605         case IDC_NFG_Edit:\r
8606             if( HIWORD(wParam) == EN_CHANGE ) {\r
8607                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8608 \r
8609                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8610             }\r
8611             return TRUE;\r
8612         case IDC_NFG_Random:\r
8613           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8614             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8615             return TRUE;\r
8616         }\r
8617 \r
8618         break;\r
8619     }\r
8620 \r
8621     return FALSE;\r
8622 }\r
8623 \r
8624 int NewGameFRC()\r
8625 {\r
8626     int result;\r
8627     int index = appData.defaultFrcPosition;\r
8628     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8629 \r
8630     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8631 \r
8632     if( result == 0 ) {\r
8633         appData.defaultFrcPosition = index;\r
8634     }\r
8635 \r
8636     return result;\r
8637 }\r
8638 \r
8639 /* [AS] Game list options. Refactored by HGM */\r
8640 \r
8641 HWND gameListOptionsDialog;\r
8642 \r
8643 // low-level front-end: clear text edit / list widget\r
8644 void\r
8645 \r
8646 GLT_ClearList()\r
8647 {\r
8648     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8649 }\r
8650 \r
8651 // low-level front-end: clear text edit / list widget\r
8652 void\r
8653 GLT_DeSelectList()\r
8654 {\r
8655     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8656 }\r
8657 \r
8658 // low-level front-end: append line to text edit / list widget\r
8659 void\r
8660 GLT_AddToList( char *name )\r
8661 {\r
8662     if( name != 0 ) {\r
8663             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8664     }\r
8665 }\r
8666 \r
8667 // low-level front-end: get line from text edit / list widget\r
8668 Boolean\r
8669 GLT_GetFromList( int index, char *name )\r
8670 {\r
8671     if( name != 0 ) {\r
8672             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8673                 return TRUE;\r
8674     }\r
8675     return FALSE;\r
8676 }\r
8677 \r
8678 void GLT_MoveSelection( HWND hDlg, int delta )\r
8679 {\r
8680     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8681     int idx2 = idx1 + delta;\r
8682     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8683 \r
8684     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8685         char buf[128];\r
8686 \r
8687         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8688         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8689         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8690         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8691     }\r
8692 }\r
8693 \r
8694 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8695 {\r
8696     switch( message )\r
8697     {\r
8698     case WM_INITDIALOG:\r
8699         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8700         \r
8701         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8702         Translate(hDlg, DLG_GameListOptions);\r
8703 \r
8704         /* Initialize list */\r
8705         GLT_TagsToList( lpUserGLT );\r
8706 \r
8707         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8708 \r
8709         break;\r
8710 \r
8711     case WM_COMMAND:\r
8712         switch( LOWORD(wParam) ) {\r
8713         case IDOK:\r
8714             GLT_ParseList();\r
8715             EndDialog( hDlg, 0 );\r
8716             return TRUE;\r
8717         case IDCANCEL:\r
8718             EndDialog( hDlg, 1 );\r
8719             return TRUE;\r
8720 \r
8721         case IDC_GLT_Default:\r
8722             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8723             return TRUE;\r
8724 \r
8725         case IDC_GLT_Restore:\r
8726             GLT_TagsToList( appData.gameListTags );\r
8727             return TRUE;\r
8728 \r
8729         case IDC_GLT_Up:\r
8730             GLT_MoveSelection( hDlg, -1 );\r
8731             return TRUE;\r
8732 \r
8733         case IDC_GLT_Down:\r
8734             GLT_MoveSelection( hDlg, +1 );\r
8735             return TRUE;\r
8736         }\r
8737 \r
8738         break;\r
8739     }\r
8740 \r
8741     return FALSE;\r
8742 }\r
8743 \r
8744 int GameListOptions()\r
8745 {\r
8746     int result;\r
8747     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8748 \r
8749       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8750 \r
8751     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8752 \r
8753     if( result == 0 ) {\r
8754         char *oldTags = appData.gameListTags;\r
8755         /* [AS] Memory leak here! */\r
8756         appData.gameListTags = strdup( lpUserGLT ); \r
8757         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8758             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8759     }\r
8760 \r
8761     return result;\r
8762 }\r
8763 \r
8764 VOID\r
8765 DisplayIcsInteractionTitle(char *str)\r
8766 {\r
8767   char consoleTitle[MSG_SIZ];\r
8768 \r
8769     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8770     SetWindowText(hwndConsole, consoleTitle);\r
8771 \r
8772     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8773       char buf[MSG_SIZ], *p = buf, *q;\r
8774         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8775       do {\r
8776         q = strchr(p, ';');\r
8777         if(q) *q++ = 0;\r
8778         if(*p) ChatPopUp(p);\r
8779       } while(p=q);\r
8780     }\r
8781 \r
8782     SetActiveWindow(hwndMain);\r
8783 }\r
8784 \r
8785 void\r
8786 DrawPosition(int fullRedraw, Board board)\r
8787 {\r
8788   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8789 }\r
8790 \r
8791 void NotifyFrontendLogin()\r
8792 {\r
8793         if (hwndConsole)\r
8794                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8795 }\r
8796 \r
8797 VOID\r
8798 ResetFrontEnd()\r
8799 {\r
8800   fromX = fromY = -1;\r
8801   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8802     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8803     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8804     dragInfo.lastpos = dragInfo.pos;\r
8805     dragInfo.start.x = dragInfo.start.y = -1;\r
8806     dragInfo.from = dragInfo.start;\r
8807     ReleaseCapture();\r
8808     DrawPosition(TRUE, NULL);\r
8809   }\r
8810   TagsPopDown();\r
8811 }\r
8812 \r
8813 \r
8814 VOID\r
8815 CommentPopUp(char *title, char *str)\r
8816 {\r
8817   HWND hwnd = GetActiveWindow();\r
8818   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8819   SAY(str);\r
8820   SetActiveWindow(hwnd);\r
8821 }\r
8822 \r
8823 VOID\r
8824 CommentPopDown(void)\r
8825 {\r
8826   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8827   if (commentDialog) {\r
8828     ShowWindow(commentDialog, SW_HIDE);\r
8829   }\r
8830   commentUp = FALSE;\r
8831 }\r
8832 \r
8833 VOID\r
8834 EditCommentPopUp(int index, char *title, char *str)\r
8835 {\r
8836   EitherCommentPopUp(index, title, str, TRUE);\r
8837 }\r
8838 \r
8839 \r
8840 int\r
8841 Roar()\r
8842 {\r
8843   MyPlaySound(&sounds[(int)SoundRoar]);\r
8844   return 1;\r
8845 }\r
8846 \r
8847 VOID\r
8848 RingBell()\r
8849 {\r
8850   MyPlaySound(&sounds[(int)SoundMove]);\r
8851 }\r
8852 \r
8853 VOID PlayIcsWinSound()\r
8854 {\r
8855   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8856 }\r
8857 \r
8858 VOID PlayIcsLossSound()\r
8859 {\r
8860   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8861 }\r
8862 \r
8863 VOID PlayIcsDrawSound()\r
8864 {\r
8865   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8866 }\r
8867 \r
8868 VOID PlayIcsUnfinishedSound()\r
8869 {\r
8870   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8871 }\r
8872 \r
8873 VOID\r
8874 PlayAlarmSound()\r
8875 {\r
8876   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8877 }\r
8878 \r
8879 VOID\r
8880 PlayTellSound()\r
8881 {\r
8882   MyPlaySound(&textAttribs[ColorTell].sound);\r
8883 }\r
8884 \r
8885 \r
8886 VOID\r
8887 EchoOn()\r
8888 {\r
8889   HWND hInput;\r
8890   consoleEcho = TRUE;\r
8891   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8892   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8893   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8894 }\r
8895 \r
8896 \r
8897 VOID\r
8898 EchoOff()\r
8899 {\r
8900   CHARFORMAT cf;\r
8901   HWND hInput;\r
8902   consoleEcho = FALSE;\r
8903   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8904   /* This works OK: set text and background both to the same color */\r
8905   cf = consoleCF;\r
8906   cf.crTextColor = COLOR_ECHOOFF;\r
8907   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8908   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8909 }\r
8910 \r
8911 /* No Raw()...? */\r
8912 \r
8913 void Colorize(ColorClass cc, int continuation)\r
8914 {\r
8915   currentColorClass = cc;\r
8916   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8917   consoleCF.crTextColor = textAttribs[cc].color;\r
8918   consoleCF.dwEffects = textAttribs[cc].effects;\r
8919   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8920 }\r
8921 \r
8922 char *\r
8923 UserName()\r
8924 {\r
8925   static char buf[MSG_SIZ];\r
8926   DWORD bufsiz = MSG_SIZ;\r
8927 \r
8928   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8929         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8930   }\r
8931   if (!GetUserName(buf, &bufsiz)) {\r
8932     /*DisplayError("Error getting user name", GetLastError());*/\r
8933     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8934   }\r
8935   return buf;\r
8936 }\r
8937 \r
8938 char *\r
8939 HostName()\r
8940 {\r
8941   static char buf[MSG_SIZ];\r
8942   DWORD bufsiz = MSG_SIZ;\r
8943 \r
8944   if (!GetComputerName(buf, &bufsiz)) {\r
8945     /*DisplayError("Error getting host name", GetLastError());*/\r
8946     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8947   }\r
8948   return buf;\r
8949 }\r
8950 \r
8951 \r
8952 int\r
8953 ClockTimerRunning()\r
8954 {\r
8955   return clockTimerEvent != 0;\r
8956 }\r
8957 \r
8958 int\r
8959 StopClockTimer()\r
8960 {\r
8961   if (clockTimerEvent == 0) return FALSE;\r
8962   KillTimer(hwndMain, clockTimerEvent);\r
8963   clockTimerEvent = 0;\r
8964   return TRUE;\r
8965 }\r
8966 \r
8967 void\r
8968 StartClockTimer(long millisec)\r
8969 {\r
8970   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8971                              (UINT) millisec, NULL);\r
8972 }\r
8973 \r
8974 void\r
8975 DisplayWhiteClock(long timeRemaining, int highlight)\r
8976 {\r
8977   HDC hdc;\r
8978   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8979 \r
8980   if(appData.noGUI) return;\r
8981   hdc = GetDC(hwndMain);\r
8982   if (!IsIconic(hwndMain)) {\r
8983     DisplayAClock(hdc, timeRemaining, highlight, \r
8984                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8985   }\r
8986   if (highlight && iconCurrent == iconBlack) {\r
8987     iconCurrent = iconWhite;\r
8988     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8989     if (IsIconic(hwndMain)) {\r
8990       DrawIcon(hdc, 2, 2, iconCurrent);\r
8991     }\r
8992   }\r
8993   (void) ReleaseDC(hwndMain, hdc);\r
8994   if (hwndConsole)\r
8995     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8996 }\r
8997 \r
8998 void\r
8999 DisplayBlackClock(long timeRemaining, int highlight)\r
9000 {\r
9001   HDC hdc;\r
9002   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9003 \r
9004 \r
9005   if(appData.noGUI) return;\r
9006   hdc = GetDC(hwndMain);\r
9007   if (!IsIconic(hwndMain)) {\r
9008     DisplayAClock(hdc, timeRemaining, highlight, \r
9009                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9010   }\r
9011   if (highlight && iconCurrent == iconWhite) {\r
9012     iconCurrent = iconBlack;\r
9013     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9014     if (IsIconic(hwndMain)) {\r
9015       DrawIcon(hdc, 2, 2, iconCurrent);\r
9016     }\r
9017   }\r
9018   (void) ReleaseDC(hwndMain, hdc);\r
9019   if (hwndConsole)\r
9020     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9021 }\r
9022 \r
9023 \r
9024 int\r
9025 LoadGameTimerRunning()\r
9026 {\r
9027   return loadGameTimerEvent != 0;\r
9028 }\r
9029 \r
9030 int\r
9031 StopLoadGameTimer()\r
9032 {\r
9033   if (loadGameTimerEvent == 0) return FALSE;\r
9034   KillTimer(hwndMain, loadGameTimerEvent);\r
9035   loadGameTimerEvent = 0;\r
9036   return TRUE;\r
9037 }\r
9038 \r
9039 void\r
9040 StartLoadGameTimer(long millisec)\r
9041 {\r
9042   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9043                                 (UINT) millisec, NULL);\r
9044 }\r
9045 \r
9046 void\r
9047 AutoSaveGame()\r
9048 {\r
9049   char *defName;\r
9050   FILE *f;\r
9051   char fileTitle[MSG_SIZ];\r
9052 \r
9053   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9054   f = OpenFileDialog(hwndMain, "a", defName,\r
9055                      appData.oldSaveStyle ? "gam" : "pgn",\r
9056                      GAME_FILT, \r
9057                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9058   if (f != NULL) {\r
9059     SaveGame(f, 0, "");\r
9060     fclose(f);\r
9061   }\r
9062 }\r
9063 \r
9064 \r
9065 void\r
9066 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9067 {\r
9068   if (delayedTimerEvent != 0) {\r
9069     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9070       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9071     }\r
9072     KillTimer(hwndMain, delayedTimerEvent);\r
9073     delayedTimerEvent = 0;\r
9074     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9075     delayedTimerCallback();\r
9076   }\r
9077   delayedTimerCallback = cb;\r
9078   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9079                                 (UINT) millisec, NULL);\r
9080 }\r
9081 \r
9082 DelayedEventCallback\r
9083 GetDelayedEvent()\r
9084 {\r
9085   if (delayedTimerEvent) {\r
9086     return delayedTimerCallback;\r
9087   } else {\r
9088     return NULL;\r
9089   }\r
9090 }\r
9091 \r
9092 void\r
9093 CancelDelayedEvent()\r
9094 {\r
9095   if (delayedTimerEvent) {\r
9096     KillTimer(hwndMain, delayedTimerEvent);\r
9097     delayedTimerEvent = 0;\r
9098   }\r
9099 }\r
9100 \r
9101 DWORD GetWin32Priority(int nice)\r
9102 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9103 /*\r
9104 REALTIME_PRIORITY_CLASS     0x00000100\r
9105 HIGH_PRIORITY_CLASS         0x00000080\r
9106 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9107 NORMAL_PRIORITY_CLASS       0x00000020\r
9108 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9109 IDLE_PRIORITY_CLASS         0x00000040\r
9110 */\r
9111         if (nice < -15) return 0x00000080;\r
9112         if (nice < 0)   return 0x00008000;\r
9113         if (nice == 0)  return 0x00000020;\r
9114         if (nice < 15)  return 0x00004000;\r
9115         return 0x00000040;\r
9116 }\r
9117 \r
9118 void RunCommand(char *cmdLine)\r
9119 {\r
9120   /* Now create the child process. */\r
9121   STARTUPINFO siStartInfo;\r
9122   PROCESS_INFORMATION piProcInfo;\r
9123 \r
9124   siStartInfo.cb = sizeof(STARTUPINFO);\r
9125   siStartInfo.lpReserved = NULL;\r
9126   siStartInfo.lpDesktop = NULL;\r
9127   siStartInfo.lpTitle = NULL;\r
9128   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9129   siStartInfo.cbReserved2 = 0;\r
9130   siStartInfo.lpReserved2 = NULL;\r
9131   siStartInfo.hStdInput = NULL;\r
9132   siStartInfo.hStdOutput = NULL;\r
9133   siStartInfo.hStdError = NULL;\r
9134 \r
9135   CreateProcess(NULL,\r
9136                 cmdLine,           /* command line */\r
9137                 NULL,      /* process security attributes */\r
9138                 NULL,      /* primary thread security attrs */\r
9139                 TRUE,      /* handles are inherited */\r
9140                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9141                 NULL,      /* use parent's environment */\r
9142                 NULL,\r
9143                 &siStartInfo, /* STARTUPINFO pointer */\r
9144                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9145 \r
9146   CloseHandle(piProcInfo.hThread);\r
9147 }\r
9148 \r
9149 /* Start a child process running the given program.\r
9150    The process's standard output can be read from "from", and its\r
9151    standard input can be written to "to".\r
9152    Exit with fatal error if anything goes wrong.\r
9153    Returns an opaque pointer that can be used to destroy the process\r
9154    later.\r
9155 */\r
9156 int\r
9157 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9158 {\r
9159 #define BUFSIZE 4096\r
9160 \r
9161   HANDLE hChildStdinRd, hChildStdinWr,\r
9162     hChildStdoutRd, hChildStdoutWr;\r
9163   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9164   SECURITY_ATTRIBUTES saAttr;\r
9165   BOOL fSuccess;\r
9166   PROCESS_INFORMATION piProcInfo;\r
9167   STARTUPINFO siStartInfo;\r
9168   ChildProc *cp;\r
9169   char buf[MSG_SIZ];\r
9170   DWORD err;\r
9171 \r
9172   if (appData.debugMode) {\r
9173     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9174   }\r
9175 \r
9176   *pr = NoProc;\r
9177 \r
9178   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9179   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9180   saAttr.bInheritHandle = TRUE;\r
9181   saAttr.lpSecurityDescriptor = NULL;\r
9182 \r
9183   /*\r
9184    * The steps for redirecting child's STDOUT:\r
9185    *     1. Create anonymous pipe to be STDOUT for child.\r
9186    *     2. Create a noninheritable duplicate of read handle,\r
9187    *         and close the inheritable read handle.\r
9188    */\r
9189 \r
9190   /* Create a pipe for the child's STDOUT. */\r
9191   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9192     return GetLastError();\r
9193   }\r
9194 \r
9195   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9196   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9197                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9198                              FALSE,     /* not inherited */\r
9199                              DUPLICATE_SAME_ACCESS);\r
9200   if (! fSuccess) {\r
9201     return GetLastError();\r
9202   }\r
9203   CloseHandle(hChildStdoutRd);\r
9204 \r
9205   /*\r
9206    * The steps for redirecting child's STDIN:\r
9207    *     1. Create anonymous pipe to be STDIN for child.\r
9208    *     2. Create a noninheritable duplicate of write handle,\r
9209    *         and close the inheritable write handle.\r
9210    */\r
9211 \r
9212   /* Create a pipe for the child's STDIN. */\r
9213   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9214     return GetLastError();\r
9215   }\r
9216 \r
9217   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9218   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9219                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9220                              FALSE,     /* not inherited */\r
9221                              DUPLICATE_SAME_ACCESS);\r
9222   if (! fSuccess) {\r
9223     return GetLastError();\r
9224   }\r
9225   CloseHandle(hChildStdinWr);\r
9226 \r
9227   /* Arrange to (1) look in dir for the child .exe file, and\r
9228    * (2) have dir be the child's working directory.  Interpret\r
9229    * dir relative to the directory WinBoard loaded from. */\r
9230   GetCurrentDirectory(MSG_SIZ, buf);\r
9231   SetCurrentDirectory(installDir);\r
9232   SetCurrentDirectory(dir);\r
9233 \r
9234   /* Now create the child process. */\r
9235 \r
9236   siStartInfo.cb = sizeof(STARTUPINFO);\r
9237   siStartInfo.lpReserved = NULL;\r
9238   siStartInfo.lpDesktop = NULL;\r
9239   siStartInfo.lpTitle = NULL;\r
9240   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9241   siStartInfo.cbReserved2 = 0;\r
9242   siStartInfo.lpReserved2 = NULL;\r
9243   siStartInfo.hStdInput = hChildStdinRd;\r
9244   siStartInfo.hStdOutput = hChildStdoutWr;\r
9245   siStartInfo.hStdError = hChildStdoutWr;\r
9246 \r
9247   fSuccess = CreateProcess(NULL,\r
9248                            cmdLine,        /* command line */\r
9249                            NULL,           /* process security attributes */\r
9250                            NULL,           /* primary thread security attrs */\r
9251                            TRUE,           /* handles are inherited */\r
9252                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9253                            NULL,           /* use parent's environment */\r
9254                            NULL,\r
9255                            &siStartInfo, /* STARTUPINFO pointer */\r
9256                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9257 \r
9258   err = GetLastError();\r
9259   SetCurrentDirectory(buf); /* return to prev directory */\r
9260   if (! fSuccess) {\r
9261     return err;\r
9262   }\r
9263 \r
9264   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9265     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9266     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9267   }\r
9268 \r
9269   /* Close the handles we don't need in the parent */\r
9270   CloseHandle(piProcInfo.hThread);\r
9271   CloseHandle(hChildStdinRd);\r
9272   CloseHandle(hChildStdoutWr);\r
9273 \r
9274   /* Prepare return value */\r
9275   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9276   cp->kind = CPReal;\r
9277   cp->hProcess = piProcInfo.hProcess;\r
9278   cp->pid = piProcInfo.dwProcessId;\r
9279   cp->hFrom = hChildStdoutRdDup;\r
9280   cp->hTo = hChildStdinWrDup;\r
9281 \r
9282   *pr = (void *) cp;\r
9283 \r
9284   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9285      2000 where engines sometimes don't see the initial command(s)\r
9286      from WinBoard and hang.  I don't understand how that can happen,\r
9287      but the Sleep is harmless, so I've put it in.  Others have also\r
9288      reported what may be the same problem, so hopefully this will fix\r
9289      it for them too.  */\r
9290   Sleep(500);\r
9291 \r
9292   return NO_ERROR;\r
9293 }\r
9294 \r
9295 \r
9296 void\r
9297 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9298 {\r
9299   ChildProc *cp; int result;\r
9300 \r
9301   cp = (ChildProc *) pr;\r
9302   if (cp == NULL) return;\r
9303 \r
9304   switch (cp->kind) {\r
9305   case CPReal:\r
9306     /* TerminateProcess is considered harmful, so... */\r
9307     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9308     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9309     /* The following doesn't work because the chess program\r
9310        doesn't "have the same console" as WinBoard.  Maybe\r
9311        we could arrange for this even though neither WinBoard\r
9312        nor the chess program uses a console for stdio? */\r
9313     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9314 \r
9315     /* [AS] Special termination modes for misbehaving programs... */\r
9316     if( signal & 8 ) { \r
9317         result = TerminateProcess( cp->hProcess, 0 );\r
9318 \r
9319         if ( appData.debugMode) {\r
9320             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9321         }\r
9322     }\r
9323     else if( signal & 4 ) {\r
9324         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9325 \r
9326         if( dw != WAIT_OBJECT_0 ) {\r
9327             result = TerminateProcess( cp->hProcess, 0 );\r
9328 \r
9329             if ( appData.debugMode) {\r
9330                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9331             }\r
9332 \r
9333         }\r
9334     }\r
9335 \r
9336     CloseHandle(cp->hProcess);\r
9337     break;\r
9338 \r
9339   case CPComm:\r
9340     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9341     break;\r
9342 \r
9343   case CPSock:\r
9344     closesocket(cp->sock);\r
9345     WSACleanup();\r
9346     break;\r
9347 \r
9348   case CPRcmd:\r
9349     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9350     closesocket(cp->sock);\r
9351     closesocket(cp->sock2);\r
9352     WSACleanup();\r
9353     break;\r
9354   }\r
9355   free(cp);\r
9356 }\r
9357 \r
9358 void\r
9359 InterruptChildProcess(ProcRef pr)\r
9360 {\r
9361   ChildProc *cp;\r
9362 \r
9363   cp = (ChildProc *) pr;\r
9364   if (cp == NULL) return;\r
9365   switch (cp->kind) {\r
9366   case CPReal:\r
9367     /* The following doesn't work because the chess program\r
9368        doesn't "have the same console" as WinBoard.  Maybe\r
9369        we could arrange for this even though neither WinBoard\r
9370        nor the chess program uses a console for stdio */\r
9371     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9372     break;\r
9373 \r
9374   case CPComm:\r
9375   case CPSock:\r
9376     /* Can't interrupt */\r
9377     break;\r
9378 \r
9379   case CPRcmd:\r
9380     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9381     break;\r
9382   }\r
9383 }\r
9384 \r
9385 \r
9386 int\r
9387 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9388 {\r
9389   char cmdLine[MSG_SIZ];\r
9390 \r
9391   if (port[0] == NULLCHAR) {\r
9392     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9393   } else {\r
9394     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9395   }\r
9396   return StartChildProcess(cmdLine, "", pr);\r
9397 }\r
9398 \r
9399 \r
9400 /* Code to open TCP sockets */\r
9401 \r
9402 int\r
9403 OpenTCP(char *host, char *port, ProcRef *pr)\r
9404 {\r
9405   ChildProc *cp;\r
9406   int err;\r
9407   SOCKET s;\r
9408 \r
9409   struct sockaddr_in sa, mysa;\r
9410   struct hostent FAR *hp;\r
9411   unsigned short uport;\r
9412   WORD wVersionRequested;\r
9413   WSADATA wsaData;\r
9414 \r
9415   /* Initialize socket DLL */\r
9416   wVersionRequested = MAKEWORD(1, 1);\r
9417   err = WSAStartup(wVersionRequested, &wsaData);\r
9418   if (err != 0) return err;\r
9419 \r
9420   /* Make socket */\r
9421   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9422     err = WSAGetLastError();\r
9423     WSACleanup();\r
9424     return err;\r
9425   }\r
9426 \r
9427   /* Bind local address using (mostly) don't-care values.\r
9428    */\r
9429   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9430   mysa.sin_family = AF_INET;\r
9431   mysa.sin_addr.s_addr = INADDR_ANY;\r
9432   uport = (unsigned short) 0;\r
9433   mysa.sin_port = htons(uport);\r
9434   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9435       == SOCKET_ERROR) {\r
9436     err = WSAGetLastError();\r
9437     WSACleanup();\r
9438     return err;\r
9439   }\r
9440 \r
9441   /* Resolve remote host name */\r
9442   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9443   if (!(hp = gethostbyname(host))) {\r
9444     unsigned int b0, b1, b2, b3;\r
9445 \r
9446     err = WSAGetLastError();\r
9447 \r
9448     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9449       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9450       hp->h_addrtype = AF_INET;\r
9451       hp->h_length = 4;\r
9452       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9453       hp->h_addr_list[0] = (char *) malloc(4);\r
9454       hp->h_addr_list[0][0] = (char) b0;\r
9455       hp->h_addr_list[0][1] = (char) b1;\r
9456       hp->h_addr_list[0][2] = (char) b2;\r
9457       hp->h_addr_list[0][3] = (char) b3;\r
9458     } else {\r
9459       WSACleanup();\r
9460       return err;\r
9461     }\r
9462   }\r
9463   sa.sin_family = hp->h_addrtype;\r
9464   uport = (unsigned short) atoi(port);\r
9465   sa.sin_port = htons(uport);\r
9466   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9467 \r
9468   /* Make connection */\r
9469   if (connect(s, (struct sockaddr *) &sa,\r
9470               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9471     err = WSAGetLastError();\r
9472     WSACleanup();\r
9473     return err;\r
9474   }\r
9475 \r
9476   /* Prepare return value */\r
9477   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9478   cp->kind = CPSock;\r
9479   cp->sock = s;\r
9480   *pr = (ProcRef *) cp;\r
9481 \r
9482   return NO_ERROR;\r
9483 }\r
9484 \r
9485 int\r
9486 OpenCommPort(char *name, ProcRef *pr)\r
9487 {\r
9488   HANDLE h;\r
9489   COMMTIMEOUTS ct;\r
9490   ChildProc *cp;\r
9491   char fullname[MSG_SIZ];\r
9492 \r
9493   if (*name != '\\')\r
9494     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9495   else\r
9496     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9497 \r
9498   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9499                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9500   if (h == (HANDLE) -1) {\r
9501     return GetLastError();\r
9502   }\r
9503   hCommPort = h;\r
9504 \r
9505   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9506 \r
9507   /* Accumulate characters until a 100ms pause, then parse */\r
9508   ct.ReadIntervalTimeout = 100;\r
9509   ct.ReadTotalTimeoutMultiplier = 0;\r
9510   ct.ReadTotalTimeoutConstant = 0;\r
9511   ct.WriteTotalTimeoutMultiplier = 0;\r
9512   ct.WriteTotalTimeoutConstant = 0;\r
9513   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9514 \r
9515   /* Prepare return value */\r
9516   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9517   cp->kind = CPComm;\r
9518   cp->hFrom = h;\r
9519   cp->hTo = h;\r
9520   *pr = (ProcRef *) cp;\r
9521 \r
9522   return NO_ERROR;\r
9523 }\r
9524 \r
9525 int\r
9526 OpenLoopback(ProcRef *pr)\r
9527 {\r
9528   DisplayFatalError(_("Not implemented"), 0, 1);\r
9529   return NO_ERROR;\r
9530 }\r
9531 \r
9532 \r
9533 int\r
9534 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9535 {\r
9536   ChildProc *cp;\r
9537   int err;\r
9538   SOCKET s, s2, s3;\r
9539   struct sockaddr_in sa, mysa;\r
9540   struct hostent FAR *hp;\r
9541   unsigned short uport;\r
9542   WORD wVersionRequested;\r
9543   WSADATA wsaData;\r
9544   int fromPort;\r
9545   char stderrPortStr[MSG_SIZ];\r
9546 \r
9547   /* Initialize socket DLL */\r
9548   wVersionRequested = MAKEWORD(1, 1);\r
9549   err = WSAStartup(wVersionRequested, &wsaData);\r
9550   if (err != 0) return err;\r
9551 \r
9552   /* Resolve remote host name */\r
9553   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9554   if (!(hp = gethostbyname(host))) {\r
9555     unsigned int b0, b1, b2, b3;\r
9556 \r
9557     err = WSAGetLastError();\r
9558 \r
9559     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9560       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9561       hp->h_addrtype = AF_INET;\r
9562       hp->h_length = 4;\r
9563       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9564       hp->h_addr_list[0] = (char *) malloc(4);\r
9565       hp->h_addr_list[0][0] = (char) b0;\r
9566       hp->h_addr_list[0][1] = (char) b1;\r
9567       hp->h_addr_list[0][2] = (char) b2;\r
9568       hp->h_addr_list[0][3] = (char) b3;\r
9569     } else {\r
9570       WSACleanup();\r
9571       return err;\r
9572     }\r
9573   }\r
9574   sa.sin_family = hp->h_addrtype;\r
9575   uport = (unsigned short) 514;\r
9576   sa.sin_port = htons(uport);\r
9577   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9578 \r
9579   /* Bind local socket to unused "privileged" port address\r
9580    */\r
9581   s = INVALID_SOCKET;\r
9582   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9583   mysa.sin_family = AF_INET;\r
9584   mysa.sin_addr.s_addr = INADDR_ANY;\r
9585   for (fromPort = 1023;; fromPort--) {\r
9586     if (fromPort < 0) {\r
9587       WSACleanup();\r
9588       return WSAEADDRINUSE;\r
9589     }\r
9590     if (s == INVALID_SOCKET) {\r
9591       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9592         err = WSAGetLastError();\r
9593         WSACleanup();\r
9594         return err;\r
9595       }\r
9596     }\r
9597     uport = (unsigned short) fromPort;\r
9598     mysa.sin_port = htons(uport);\r
9599     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9600         == SOCKET_ERROR) {\r
9601       err = WSAGetLastError();\r
9602       if (err == WSAEADDRINUSE) continue;\r
9603       WSACleanup();\r
9604       return err;\r
9605     }\r
9606     if (connect(s, (struct sockaddr *) &sa,\r
9607       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9608       err = WSAGetLastError();\r
9609       if (err == WSAEADDRINUSE) {\r
9610         closesocket(s);\r
9611         s = -1;\r
9612         continue;\r
9613       }\r
9614       WSACleanup();\r
9615       return err;\r
9616     }\r
9617     break;\r
9618   }\r
9619 \r
9620   /* Bind stderr local socket to unused "privileged" port address\r
9621    */\r
9622   s2 = INVALID_SOCKET;\r
9623   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9624   mysa.sin_family = AF_INET;\r
9625   mysa.sin_addr.s_addr = INADDR_ANY;\r
9626   for (fromPort = 1023;; fromPort--) {\r
9627     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9628     if (fromPort < 0) {\r
9629       (void) closesocket(s);\r
9630       WSACleanup();\r
9631       return WSAEADDRINUSE;\r
9632     }\r
9633     if (s2 == INVALID_SOCKET) {\r
9634       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9635         err = WSAGetLastError();\r
9636         closesocket(s);\r
9637         WSACleanup();\r
9638         return err;\r
9639       }\r
9640     }\r
9641     uport = (unsigned short) fromPort;\r
9642     mysa.sin_port = htons(uport);\r
9643     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9644         == SOCKET_ERROR) {\r
9645       err = WSAGetLastError();\r
9646       if (err == WSAEADDRINUSE) continue;\r
9647       (void) closesocket(s);\r
9648       WSACleanup();\r
9649       return err;\r
9650     }\r
9651     if (listen(s2, 1) == SOCKET_ERROR) {\r
9652       err = WSAGetLastError();\r
9653       if (err == WSAEADDRINUSE) {\r
9654         closesocket(s2);\r
9655         s2 = INVALID_SOCKET;\r
9656         continue;\r
9657       }\r
9658       (void) closesocket(s);\r
9659       (void) closesocket(s2);\r
9660       WSACleanup();\r
9661       return err;\r
9662     }\r
9663     break;\r
9664   }\r
9665   prevStderrPort = fromPort; // remember port used\r
9666   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9667 \r
9668   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9669     err = WSAGetLastError();\r
9670     (void) closesocket(s);\r
9671     (void) closesocket(s2);\r
9672     WSACleanup();\r
9673     return err;\r
9674   }\r
9675 \r
9676   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9677     err = WSAGetLastError();\r
9678     (void) closesocket(s);\r
9679     (void) closesocket(s2);\r
9680     WSACleanup();\r
9681     return err;\r
9682   }\r
9683   if (*user == NULLCHAR) user = UserName();\r
9684   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9685     err = WSAGetLastError();\r
9686     (void) closesocket(s);\r
9687     (void) closesocket(s2);\r
9688     WSACleanup();\r
9689     return err;\r
9690   }\r
9691   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9692     err = WSAGetLastError();\r
9693     (void) closesocket(s);\r
9694     (void) closesocket(s2);\r
9695     WSACleanup();\r
9696     return err;\r
9697   }\r
9698 \r
9699   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9700     err = WSAGetLastError();\r
9701     (void) closesocket(s);\r
9702     (void) closesocket(s2);\r
9703     WSACleanup();\r
9704     return err;\r
9705   }\r
9706   (void) closesocket(s2);  /* Stop listening */\r
9707 \r
9708   /* Prepare return value */\r
9709   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9710   cp->kind = CPRcmd;\r
9711   cp->sock = s;\r
9712   cp->sock2 = s3;\r
9713   *pr = (ProcRef *) cp;\r
9714 \r
9715   return NO_ERROR;\r
9716 }\r
9717 \r
9718 \r
9719 InputSourceRef\r
9720 AddInputSource(ProcRef pr, int lineByLine,\r
9721                InputCallback func, VOIDSTAR closure)\r
9722 {\r
9723   InputSource *is, *is2 = NULL;\r
9724   ChildProc *cp = (ChildProc *) pr;\r
9725 \r
9726   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9727   is->lineByLine = lineByLine;\r
9728   is->func = func;\r
9729   is->closure = closure;\r
9730   is->second = NULL;\r
9731   is->next = is->buf;\r
9732   if (pr == NoProc) {\r
9733     is->kind = CPReal;\r
9734     consoleInputSource = is;\r
9735   } else {\r
9736     is->kind = cp->kind;\r
9737     /* \r
9738         [AS] Try to avoid a race condition if the thread is given control too early:\r
9739         we create all threads suspended so that the is->hThread variable can be\r
9740         safely assigned, then let the threads start with ResumeThread.\r
9741     */\r
9742     switch (cp->kind) {\r
9743     case CPReal:\r
9744       is->hFile = cp->hFrom;\r
9745       cp->hFrom = NULL; /* now owned by InputThread */\r
9746       is->hThread =\r
9747         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9748                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9749       break;\r
9750 \r
9751     case CPComm:\r
9752       is->hFile = cp->hFrom;\r
9753       cp->hFrom = NULL; /* now owned by InputThread */\r
9754       is->hThread =\r
9755         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9756                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9757       break;\r
9758 \r
9759     case CPSock:\r
9760       is->sock = cp->sock;\r
9761       is->hThread =\r
9762         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9763                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9764       break;\r
9765 \r
9766     case CPRcmd:\r
9767       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9768       *is2 = *is;\r
9769       is->sock = cp->sock;\r
9770       is->second = is2;\r
9771       is2->sock = cp->sock2;\r
9772       is2->second = is2;\r
9773       is->hThread =\r
9774         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9775                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9776       is2->hThread =\r
9777         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9778                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9779       break;\r
9780     }\r
9781 \r
9782     if( is->hThread != NULL ) {\r
9783         ResumeThread( is->hThread );\r
9784     }\r
9785 \r
9786     if( is2 != NULL && is2->hThread != NULL ) {\r
9787         ResumeThread( is2->hThread );\r
9788     }\r
9789   }\r
9790 \r
9791   return (InputSourceRef) is;\r
9792 }\r
9793 \r
9794 void\r
9795 RemoveInputSource(InputSourceRef isr)\r
9796 {\r
9797   InputSource *is;\r
9798 \r
9799   is = (InputSource *) isr;\r
9800   is->hThread = NULL;  /* tell thread to stop */\r
9801   CloseHandle(is->hThread);\r
9802   if (is->second != NULL) {\r
9803     is->second->hThread = NULL;\r
9804     CloseHandle(is->second->hThread);\r
9805   }\r
9806 }\r
9807 \r
9808 int no_wrap(char *message, int count)\r
9809 {\r
9810     ConsoleOutput(message, count, FALSE);\r
9811     return count;\r
9812 }\r
9813 \r
9814 int\r
9815 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9816 {\r
9817   DWORD dOutCount;\r
9818   int outCount = SOCKET_ERROR;\r
9819   ChildProc *cp = (ChildProc *) pr;\r
9820   static OVERLAPPED ovl;\r
9821 \r
9822   static int line = 0;\r
9823 \r
9824   if (pr == NoProc)\r
9825   {\r
9826     if (appData.noJoin || !appData.useInternalWrap)\r
9827       return no_wrap(message, count);\r
9828     else\r
9829     {\r
9830       int width = get_term_width();\r
9831       int len = wrap(NULL, message, count, width, &line);\r
9832       char *msg = malloc(len);\r
9833       int dbgchk;\r
9834 \r
9835       if (!msg)\r
9836         return no_wrap(message, count);\r
9837       else\r
9838       {\r
9839         dbgchk = wrap(msg, message, count, width, &line);\r
9840         if (dbgchk != len && appData.debugMode)\r
9841             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9842         ConsoleOutput(msg, len, FALSE);\r
9843         free(msg);\r
9844         return len;\r
9845       }\r
9846     }\r
9847   }\r
9848 \r
9849   if (ovl.hEvent == NULL) {\r
9850     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9851   }\r
9852   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9853 \r
9854   switch (cp->kind) {\r
9855   case CPSock:\r
9856   case CPRcmd:\r
9857     outCount = send(cp->sock, message, count, 0);\r
9858     if (outCount == SOCKET_ERROR) {\r
9859       *outError = WSAGetLastError();\r
9860     } else {\r
9861       *outError = NO_ERROR;\r
9862     }\r
9863     break;\r
9864 \r
9865   case CPReal:\r
9866     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9867                   &dOutCount, NULL)) {\r
9868       *outError = NO_ERROR;\r
9869       outCount = (int) dOutCount;\r
9870     } else {\r
9871       *outError = GetLastError();\r
9872     }\r
9873     break;\r
9874 \r
9875   case CPComm:\r
9876     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9877                             &dOutCount, &ovl);\r
9878     if (*outError == NO_ERROR) {\r
9879       outCount = (int) dOutCount;\r
9880     }\r
9881     break;\r
9882   }\r
9883   return outCount;\r
9884 }\r
9885 \r
9886 void\r
9887 DoSleep(int n)\r
9888 {\r
9889     if(n != 0) Sleep(n);\r
9890 }\r
9891 \r
9892 int\r
9893 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9894                        long msdelay)\r
9895 {\r
9896   /* Ignore delay, not implemented for WinBoard */\r
9897   return OutputToProcess(pr, message, count, outError);\r
9898 }\r
9899 \r
9900 \r
9901 void\r
9902 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9903                         char *buf, int count, int error)\r
9904 {\r
9905   DisplayFatalError(_("Not implemented"), 0, 1);\r
9906 }\r
9907 \r
9908 /* see wgamelist.c for Game List functions */\r
9909 /* see wedittags.c for Edit Tags functions */\r
9910 \r
9911 \r
9912 int\r
9913 ICSInitScript()\r
9914 {\r
9915   FILE *f;\r
9916   char buf[MSG_SIZ];\r
9917   char *dummy;\r
9918 \r
9919   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9920     f = fopen(buf, "r");\r
9921     if (f != NULL) {\r
9922       ProcessICSInitScript(f);\r
9923       fclose(f);\r
9924       return TRUE;\r
9925     }\r
9926   }\r
9927   return FALSE;\r
9928 }\r
9929 \r
9930 \r
9931 VOID\r
9932 StartAnalysisClock()\r
9933 {\r
9934   if (analysisTimerEvent) return;\r
9935   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9936                                         (UINT) 2000, NULL);\r
9937 }\r
9938 \r
9939 VOID\r
9940 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9941 {\r
9942   highlightInfo.sq[0].x = fromX;\r
9943   highlightInfo.sq[0].y = fromY;\r
9944   highlightInfo.sq[1].x = toX;\r
9945   highlightInfo.sq[1].y = toY;\r
9946 }\r
9947 \r
9948 VOID\r
9949 ClearHighlights()\r
9950 {\r
9951   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9952     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9953 }\r
9954 \r
9955 VOID\r
9956 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9957 {\r
9958   premoveHighlightInfo.sq[0].x = fromX;\r
9959   premoveHighlightInfo.sq[0].y = fromY;\r
9960   premoveHighlightInfo.sq[1].x = toX;\r
9961   premoveHighlightInfo.sq[1].y = toY;\r
9962 }\r
9963 \r
9964 VOID\r
9965 ClearPremoveHighlights()\r
9966 {\r
9967   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9968     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9969 }\r
9970 \r
9971 VOID\r
9972 ShutDownFrontEnd()\r
9973 {\r
9974   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9975   DeleteClipboardTempFiles();\r
9976 }\r
9977 \r
9978 void\r
9979 BoardToTop()\r
9980 {\r
9981     if (IsIconic(hwndMain))\r
9982       ShowWindow(hwndMain, SW_RESTORE);\r
9983 \r
9984     SetActiveWindow(hwndMain);\r
9985 }\r
9986 \r
9987 /*\r
9988  * Prototypes for animation support routines\r
9989  */\r
9990 static void ScreenSquare(int column, int row, POINT * pt);\r
9991 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9992      POINT frames[], int * nFrames);\r
9993 \r
9994 \r
9995 #define kFactor 4\r
9996 \r
9997 void\r
9998 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9999 {       // [HGM] atomic: animate blast wave\r
10000         int i;\r
10001 \r
10002         explodeInfo.fromX = fromX;\r
10003         explodeInfo.fromY = fromY;\r
10004         explodeInfo.toX = toX;\r
10005         explodeInfo.toY = toY;\r
10006         for(i=1; i<4*kFactor; i++) {\r
10007             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10008             DrawPosition(FALSE, board);\r
10009             Sleep(appData.animSpeed);\r
10010         }\r
10011         explodeInfo.radius = 0;\r
10012         DrawPosition(TRUE, board);\r
10013 }\r
10014 \r
10015 void\r
10016 AnimateMove(board, fromX, fromY, toX, toY)\r
10017      Board board;\r
10018      int fromX;\r
10019      int fromY;\r
10020      int toX;\r
10021      int toY;\r
10022 {\r
10023   ChessSquare piece;\r
10024   int x = toX, y = toY;\r
10025   POINT start, finish, mid;\r
10026   POINT frames[kFactor * 2 + 1];\r
10027   int nFrames, n;\r
10028 \r
10029   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10030 \r
10031   if (!appData.animate) return;\r
10032   if (doingSizing) return;\r
10033   if (fromY < 0 || fromX < 0) return;\r
10034   piece = board[fromY][fromX];\r
10035   if (piece >= EmptySquare) return;\r
10036 \r
10037   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10038 \r
10039 again:\r
10040 \r
10041   ScreenSquare(fromX, fromY, &start);\r
10042   ScreenSquare(toX, toY, &finish);\r
10043 \r
10044   /* All moves except knight jumps move in straight line */\r
10045   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10046     mid.x = start.x + (finish.x - start.x) / 2;\r
10047     mid.y = start.y + (finish.y - start.y) / 2;\r
10048   } else {\r
10049     /* Knight: make straight movement then diagonal */\r
10050     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10051        mid.x = start.x + (finish.x - start.x) / 2;\r
10052        mid.y = start.y;\r
10053      } else {\r
10054        mid.x = start.x;\r
10055        mid.y = start.y + (finish.y - start.y) / 2;\r
10056      }\r
10057   }\r
10058   \r
10059   /* Don't use as many frames for very short moves */\r
10060   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10061     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10062   else\r
10063     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10064 \r
10065   animInfo.from.x = fromX;\r
10066   animInfo.from.y = fromY;\r
10067   animInfo.to.x = toX;\r
10068   animInfo.to.y = toY;\r
10069   animInfo.lastpos = start;\r
10070   animInfo.piece = piece;\r
10071   for (n = 0; n < nFrames; n++) {\r
10072     animInfo.pos = frames[n];\r
10073     DrawPosition(FALSE, NULL);\r
10074     animInfo.lastpos = animInfo.pos;\r
10075     Sleep(appData.animSpeed);\r
10076   }\r
10077   animInfo.pos = finish;\r
10078   DrawPosition(FALSE, NULL);\r
10079 \r
10080   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10081 \r
10082   animInfo.piece = EmptySquare;\r
10083   Explode(board, fromX, fromY, toX, toY);\r
10084 }\r
10085 \r
10086 /*      Convert board position to corner of screen rect and color       */\r
10087 \r
10088 static void\r
10089 ScreenSquare(column, row, pt)\r
10090      int column; int row; POINT * pt;\r
10091 {\r
10092   if (flipView) {\r
10093     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10094     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10095   } else {\r
10096     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10097     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10098   }\r
10099 }\r
10100 \r
10101 /*      Generate a series of frame coords from start->mid->finish.\r
10102         The movement rate doubles until the half way point is\r
10103         reached, then halves back down to the final destination,\r
10104         which gives a nice slow in/out effect. The algorithmn\r
10105         may seem to generate too many intermediates for short\r
10106         moves, but remember that the purpose is to attract the\r
10107         viewers attention to the piece about to be moved and\r
10108         then to where it ends up. Too few frames would be less\r
10109         noticeable.                                             */\r
10110 \r
10111 static void\r
10112 Tween(start, mid, finish, factor, frames, nFrames)\r
10113      POINT * start; POINT * mid;\r
10114      POINT * finish; int factor;\r
10115      POINT frames[]; int * nFrames;\r
10116 {\r
10117   int n, fraction = 1, count = 0;\r
10118 \r
10119   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10120   for (n = 0; n < factor; n++)\r
10121     fraction *= 2;\r
10122   for (n = 0; n < factor; n++) {\r
10123     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10124     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10125     count ++;\r
10126     fraction = fraction / 2;\r
10127   }\r
10128   \r
10129   /* Midpoint */\r
10130   frames[count] = *mid;\r
10131   count ++;\r
10132   \r
10133   /* Slow out, stepping 1/2, then 1/4, ... */\r
10134   fraction = 2;\r
10135   for (n = 0; n < factor; n++) {\r
10136     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10137     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10138     count ++;\r
10139     fraction = fraction * 2;\r
10140   }\r
10141   *nFrames = count;\r
10142 }\r
10143 \r
10144 void\r
10145 SettingsPopUp(ChessProgramState *cps)\r
10146 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10147       EngineOptionsPopup(savedHwnd, cps);\r
10148 }\r
10149 \r
10150 int flock(int fid, int code)\r
10151 {\r
10152     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10153     OVERLAPPED ov;\r
10154     ov.hEvent = NULL;\r
10155     ov.Offset = 0;\r
10156     ov.OffsetHigh = 0;\r
10157     switch(code) {\r
10158       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10159 \r
10160       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10161       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10162       default: return -1;\r
10163     }\r
10164     return 0;\r
10165 }\r
10166 \r
10167 char *\r
10168 Col2Text (int n)\r
10169 {\r
10170     static int i=0;\r
10171     static char col[8][20];\r
10172     COLORREF color = *(COLORREF *) colorVariable[n];\r
10173     i = i+1 & 7;\r
10174     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10175     return col[i];\r
10176 }\r
10177 \r
10178 void\r
10179 ActivateTheme (int new)\r
10180 {   // Redo initialization of features depending on options that can occur in themes\r
10181    InitTextures();\r
10182    if(new) InitDrawingColors();\r
10183    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10184    InitDrawingSizes(boardSize, 0);\r
10185    InvalidateRect(hwndMain, NULL, TRUE);\r
10186 }\r