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