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