Updated copyright notice to 2014
[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 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 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
116 void ChatPopUp P((char *s));\r
117 typedef struct {\r
118   ChessSquare piece;  \r
119   POINT pos;      /* window coordinates of current pos */\r
120   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
121   POINT from;     /* board coordinates of the piece's orig pos */\r
122   POINT to;       /* board coordinates of the piece's new pos */\r
123 } AnimInfo;\r
124 \r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT start;    /* window coordinates of start pos */\r
129   POINT pos;      /* window coordinates of current pos */\r
130   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
131   POINT from;     /* board coordinates of the piece's orig pos */\r
132   ChessSquare piece;\r
133 } DragInfo;\r
134 \r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
136 \r
137 typedef struct {\r
138   POINT sq[2];    /* board coordinates of from, to squares */\r
139 } HighlightInfo;\r
140 \r
141 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
144 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
145 \r
146 typedef struct { // [HGM] atomic\r
147   int fromX, fromY, toX, toY, radius;\r
148 } ExplodeInfo;\r
149 \r
150 static ExplodeInfo explodeInfo;\r
151 \r
152 /* Window class names */\r
153 char szAppName[] = "WinBoard";\r
154 char szConsoleName[] = "WBConsole";\r
155 \r
156 /* Title bar text */\r
157 char szTitle[] = "WinBoard";\r
158 char szConsoleTitle[] = "I C S Interaction";\r
159 \r
160 char *programName;\r
161 char *settingsFileName;\r
162 Boolean saveSettingsOnExit;\r
163 char installDir[MSG_SIZ];\r
164 int errorExitStatus;\r
165 \r
166 BoardSize boardSize;\r
167 Boolean chessProgram;\r
168 //static int boardX, boardY;\r
169 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
170 int squareSize, lineGap, minorSize, border;\r
171 static int winW, winH;\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
173 static int logoHeight = 0;\r
174 static char messageText[MESSAGE_TEXT_MAX];\r
175 static int clockTimerEvent = 0;\r
176 static int loadGameTimerEvent = 0;\r
177 static int analysisTimerEvent = 0;\r
178 static DelayedEventCallback delayedTimerCallback;\r
179 static int delayedTimerEvent = 0;\r
180 static int buttonCount = 2;\r
181 char *icsTextMenuString;\r
182 char *icsNames;\r
183 char *firstChessProgramNames;\r
184 char *secondChessProgramNames;\r
185 \r
186 #define PALETTESIZE 256\r
187 \r
188 HINSTANCE hInst;          /* current instance */\r
189 Boolean alwaysOnTop = FALSE;\r
190 RECT boardRect;\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
192   blackPieceColor, highlightSquareColor, premoveHighlightColor;\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,      /* [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 \r
230 \r
231 #if defined(_winmajor)\r
232 #define oldDialog (_winmajor < 4)\r
233 #else\r
234 #define oldDialog 0\r
235 #endif\r
236 #endif\r
237 \r
238 #define INTERNATIONAL\r
239 \r
240 #ifdef INTERNATIONAL\r
241 #  define _(s) T_(s)\r
242 #  define N_(s) s\r
243 #else\r
244 #  define _(s) s\r
245 #  define N_(s) s\r
246 #  define T_(s) s\r
247 #  define Translate(x, y)\r
248 #  define LoadLanguageFile(s)\r
249 #endif\r
250 \r
251 #ifdef INTERNATIONAL\r
252 \r
253 Boolean barbaric; // flag indicating if translation is needed\r
254 \r
255 // list of item numbers used in each dialog (used to alter language at run time)\r
256 \r
257 #define ABOUTBOX -1  /* not sure why these are needed */\r
258 #define ABOUTBOX2 -1\r
259 \r
260 int dialogItems[][42] = {\r
261 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
262 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
263   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
264 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
265   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
267   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
270   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
273   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
274 { ABOUTBOX2, IDC_ChessBoard }, \r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
276   OPT_GameListClose, IDC_GameListDoFilter }, \r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
278 { DLG_Error, IDOK }, \r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
280   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
283   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
284   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
285 { DLG_IndexNumber, IDC_Index }, \r
286 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
287 { DLG_TypeInName, IDOK, IDCANCEL }, \r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
289   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
291   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
292   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
293   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
294   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
295   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
296   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
298   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
299   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
300   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
301   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
302   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
303   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
304   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
305   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
307   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
308   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
309   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
310   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
311   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
312   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
313   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
315   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
316   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
317   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
318   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
319   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
320   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
321   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
322   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
324   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
325   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
326   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
327   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
330   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
331 { DLG_MoveHistory }, \r
332 { DLG_EvalGraph }, \r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
336   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
337   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
338   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
340   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
341   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
342 { 0 }\r
343 };\r
344 \r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
346 static int lastChecked;\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
348 extern int tinyLayout;\r
349 extern char * menuBarText[][10];\r
350 \r
351 void\r
352 LoadLanguageFile(char *name)\r
353 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
354     FILE *f;\r
355     int i=0, j=0, n=0, k;\r
356     char buf[MSG_SIZ];\r
357 \r
358     if(!name || name[0] == NULLCHAR) return;\r
359       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
360     appData.language = oldLanguage;\r
361     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
362     if((f = fopen(buf, "r")) == NULL) return;\r
363     while((k = fgetc(f)) != EOF) {\r
364         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
365         languageBuf[i] = k;\r
366         if(k == '\n') {\r
367             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
368                 char *p;\r
369                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
370                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
371                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
372                         english[j] = languageBuf + n + 1; *p = 0;\r
373                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
375                     }\r
376                 }\r
377             }\r
378             n = i + 1;\r
379         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
380             switch(k) {\r
381               case 'n': k = '\n'; break;\r
382               case 'r': k = '\r'; break;\r
383               case 't': k = '\t'; break;\r
384             }\r
385             languageBuf[--i] = k;\r
386         }\r
387         i++;\r
388     }\r
389     fclose(f);\r
390     barbaric = (j != 0);\r
391     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
392 }\r
393 \r
394 char *\r
395 T_(char *s)\r
396 {   // return the translation of the given string\r
397     // efficiency can be improved a lot...\r
398     int i=0;\r
399     static char buf[MSG_SIZ];\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
401     if(!barbaric) return s;\r
402     if(!s) return ""; // sanity\r
403     while(english[i]) {\r
404         if(!strcmp(s, english[i])) return foreign[i];\r
405         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
406             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
407             return buf;\r
408         }\r
409         i++;\r
410     }\r
411     return s;\r
412 }\r
413 \r
414 void\r
415 Translate(HWND hDlg, int dialogID)\r
416 {   // translate all text items in the given dialog\r
417     int i=0, j, k;\r
418     char buf[MSG_SIZ], *s;\r
419     if(!barbaric) return;\r
420     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
421     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
422     GetWindowText( hDlg, buf, MSG_SIZ );\r
423     s = T_(buf);\r
424     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
425     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
426         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
427         if(strlen(buf) == 0) continue;\r
428         s = T_(buf);\r
429         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
430     }\r
431 }\r
432 \r
433 HMENU\r
434 TranslateOneMenu(int i, HMENU subMenu)\r
435 {\r
436     int j;\r
437     static MENUITEMINFO info;\r
438 \r
439     info.cbSize = sizeof(MENUITEMINFO);\r
440     info.fMask = MIIM_STATE | MIIM_TYPE;\r
441           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
442             char buf[MSG_SIZ];\r
443             info.dwTypeData = buf;\r
444             info.cch = sizeof(buf);\r
445             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
446             if(i < 10) {\r
447                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
448                 else menuText[i][j] = strdup(buf); // remember original on first change\r
449             }\r
450             if(buf[0] == NULLCHAR) continue;\r
451             info.dwTypeData = T_(buf);\r
452             info.cch = strlen(buf)+1;\r
453             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
454           }\r
455     return subMenu;\r
456 }\r
457 \r
458 void\r
459 TranslateMenus(int addLanguage)\r
460 {\r
461     int i;\r
462     WIN32_FIND_DATA fileData;\r
463     HANDLE hFind;\r
464 #define IDM_English 1970\r
465     if(1) {\r
466         HMENU mainMenu = GetMenu(hwndMain);\r
467         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
468           HMENU subMenu = GetSubMenu(mainMenu, i);\r
469           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
470                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
471           TranslateOneMenu(i, subMenu);\r
472         }\r
473         DrawMenuBar(hwndMain);\r
474     }\r
475 \r
476     if(!addLanguage) return;\r
477     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
478         HMENU mainMenu = GetMenu(hwndMain);\r
479         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
480         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
481         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
482         i = 0; lastChecked = IDM_English;\r
483         do {\r
484             char *p, *q = fileData.cFileName;\r
485             int checkFlag = MF_UNCHECKED;\r
486             languageFile[i] = strdup(q);\r
487             if(barbaric && !strcmp(oldLanguage, q)) {\r
488                 checkFlag = MF_CHECKED;\r
489                 lastChecked = IDM_English + i + 1;\r
490                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
491             }\r
492             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
493             p = strstr(fileData.cFileName, ".lng");\r
494             if(p) *p = 0;\r
495             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
496         } while(FindNextFile(hFind, &fileData));\r
497         FindClose(hFind);\r
498     }\r
499 }\r
500 \r
501 #endif\r
502 \r
503 #define IDM_RecentEngines 3000\r
504 \r
505 void\r
506 RecentEngineMenu (char *s)\r
507 {\r
508     if(appData.icsActive) return;\r
509     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
510         HMENU mainMenu = GetMenu(hwndMain);\r
511         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
512         int i=IDM_RecentEngines;\r
513         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
514         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
515         while(*s) {\r
516           char *p = strchr(s, '\n');\r
517           if(p == NULL) return; // malformed!\r
518           *p = NULLCHAR;\r
519           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
520           *p = '\n';\r
521           s = p+1;\r
522         }\r
523     }\r
524 }\r
525 \r
526 \r
527 typedef struct {\r
528   char *name;\r
529   int squareSize;\r
530   int lineGap;\r
531   int smallLayout;\r
532   int tinyLayout;\r
533   int cliWidth, cliHeight;\r
534 } SizeInfo;\r
535 \r
536 SizeInfo sizeInfo[] = \r
537 {\r
538   { "tiny",     21, 0, 1, 1, 0, 0 },\r
539   { "teeny",    25, 1, 1, 1, 0, 0 },\r
540   { "dinky",    29, 1, 1, 1, 0, 0 },\r
541   { "petite",   33, 1, 1, 1, 0, 0 },\r
542   { "slim",     37, 2, 1, 0, 0, 0 },\r
543   { "small",    40, 2, 1, 0, 0, 0 },\r
544   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
545   { "middling", 49, 2, 0, 0, 0, 0 },\r
546   { "average",  54, 2, 0, 0, 0, 0 },\r
547   { "moderate", 58, 3, 0, 0, 0, 0 },\r
548   { "medium",   64, 3, 0, 0, 0, 0 },\r
549   { "bulky",    72, 3, 0, 0, 0, 0 },\r
550   { "large",    80, 3, 0, 0, 0, 0 },\r
551   { "big",      87, 3, 0, 0, 0, 0 },\r
552   { "huge",     95, 3, 0, 0, 0, 0 },\r
553   { "giant",    108, 3, 0, 0, 0, 0 },\r
554   { "colossal", 116, 4, 0, 0, 0, 0 },\r
555   { "titanic",  129, 4, 0, 0, 0, 0 },\r
556   { NULL, 0, 0, 0, 0, 0, 0 }\r
557 };\r
558 \r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
561 {\r
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579   { 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
580 };\r
581 \r
582 MyFont *font[NUM_SIZES][NUM_FONTS];\r
583 \r
584 typedef struct {\r
585   char *label;\r
586   int id;\r
587   HWND hwnd;\r
588   WNDPROC wndproc;\r
589 } MyButtonDesc;\r
590 \r
591 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
592 #define N_BUTTONS 5\r
593 \r
594 MyButtonDesc buttonDesc[N_BUTTONS] =\r
595 {\r
596   {"<<", IDM_ToStart, NULL, NULL},\r
597   {"<", IDM_Backward, NULL, NULL},\r
598   {"P", IDM_Pause, NULL, NULL},\r
599   {">", IDM_Forward, NULL, NULL},\r
600   {">>", IDM_ToEnd, NULL, NULL},\r
601 };\r
602 \r
603 int tinyLayout = 0, smallLayout = 0;\r
604 #define MENU_BAR_ITEMS 9\r
605 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
606   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), 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 int APIENTRY\r
791 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
792         LPSTR lpCmdLine, int nCmdShow)\r
793 {\r
794   MSG msg;\r
795   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
796 //  INITCOMMONCONTROLSEX ex;\r
797 \r
798   debugFP = stderr;\r
799 \r
800   LoadLibrary("RICHED32.DLL");\r
801   consoleCF.cbSize = sizeof(CHARFORMAT);\r
802 \r
803   if (!InitApplication(hInstance)) {\r
804     return (FALSE);\r
805   }\r
806   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
807     return (FALSE);\r
808   }\r
809 \r
810   JAWS_INIT\r
811   TranslateMenus(1);\r
812 \r
813 //  InitCommonControlsEx(&ex);\r
814   InitCommonControls();\r
815 \r
816   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
817   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
818   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
819 \r
820   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
821 \r
822   while (GetMessage(&msg, /* message structure */\r
823                     NULL, /* handle of window receiving the message */\r
824                     0,    /* lowest message to examine */\r
825                     0))   /* highest message to examine */\r
826     {\r
827 \r
828       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
829         // [HGM] navigate: switch between all windows with tab\r
830         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
831         int i, currentElement = 0;\r
832 \r
833         // first determine what element of the chain we come from (if any)\r
834         if(appData.icsActive) {\r
835             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
836             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
837         }\r
838         if(engineOutputDialog && EngineOutputIsUp()) {\r
839             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
840             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
841         }\r
842         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
843             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
844         }\r
845         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
846         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
847         if(msg.hwnd == e1)                 currentElement = 2; else\r
848         if(msg.hwnd == e2)                 currentElement = 3; else\r
849         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
850         if(msg.hwnd == mh)                currentElement = 4; else\r
851         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
852         if(msg.hwnd == hText)  currentElement = 5; else\r
853         if(msg.hwnd == hInput) currentElement = 6; else\r
854         for (i = 0; i < N_BUTTONS; i++) {\r
855             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
856         }\r
857 \r
858         // determine where to go to\r
859         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
860           do {\r
861             currentElement = (currentElement + direction) % 7;\r
862             switch(currentElement) {\r
863                 case 0:\r
864                   h = hwndMain; break; // passing this case always makes the loop exit\r
865                 case 1:\r
866                   h = buttonDesc[0].hwnd; break; // could be NULL\r
867                 case 2:\r
868                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
869                   h = e1; break;\r
870                 case 3:\r
871                   if(!EngineOutputIsUp()) continue;\r
872                   h = e2; break;\r
873                 case 4:\r
874                   if(!MoveHistoryIsUp()) continue;\r
875                   h = mh; break;\r
876 //              case 6: // input to eval graph does not seem to get here!\r
877 //                if(!EvalGraphIsUp()) continue;\r
878 //                h = evalGraphDialog; break;\r
879                 case 5:\r
880                   if(!appData.icsActive) continue;\r
881                   SAY("display");\r
882                   h = hText; break;\r
883                 case 6:\r
884                   if(!appData.icsActive) continue;\r
885                   SAY("input");\r
886                   h = hInput; break;\r
887             }\r
888           } while(h == 0);\r
889 \r
890           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
891           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
892           SetFocus(h);\r
893 \r
894           continue; // this message now has been processed\r
895         }\r
896       }\r
897 \r
898       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
899           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
900           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
901           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
902           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
903           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
904           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
905           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
906           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
907           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
908         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
909         for(i=0; i<MAX_CHAT; i++) \r
910             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
911                 done = 1; break;\r
912         }\r
913         if(done) continue; // [HGM] chat: end patch\r
914         TranslateMessage(&msg); /* Translates virtual key codes */\r
915         DispatchMessage(&msg);  /* Dispatches message to window */\r
916       }\r
917     }\r
918 \r
919 \r
920   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
921 }\r
922 \r
923 /*---------------------------------------------------------------------------*\\r
924  *\r
925  * Initialization functions\r
926  *\r
927 \*---------------------------------------------------------------------------*/\r
928 \r
929 void\r
930 SetUserLogo()\r
931 {   // update user logo if necessary\r
932     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
933 \r
934     if(appData.autoLogo) {\r
935           curName = UserName();\r
936           if(strcmp(curName, oldUserName)) {\r
937                 GetCurrentDirectory(MSG_SIZ, dir);\r
938                 SetCurrentDirectory(installDir);\r
939                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
940                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
941                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
942                 if(userLogo == NULL)\r
943                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
944                 SetCurrentDirectory(dir); /* return to prev directory */\r
945           }\r
946     }\r
947 }\r
948 \r
949 BOOL\r
950 InitApplication(HINSTANCE hInstance)\r
951 {\r
952   WNDCLASS wc;\r
953 \r
954   /* Fill in window class structure with parameters that describe the */\r
955   /* main window. */\r
956 \r
957   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
958   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
959   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
960   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
961   wc.hInstance     = hInstance;         /* Owner of this class */\r
962   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
963   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
964   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
965   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
966   wc.lpszClassName = szAppName;                 /* Name to register as */\r
967 \r
968   /* Register the window class and return success/failure code. */\r
969   if (!RegisterClass(&wc)) return FALSE;\r
970 \r
971   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
972   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
973   wc.cbClsExtra    = 0;\r
974   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
975   wc.hInstance     = hInstance;\r
976   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
977   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
978   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
979   wc.lpszMenuName  = NULL;\r
980   wc.lpszClassName = szConsoleName;\r
981 \r
982   if (!RegisterClass(&wc)) return FALSE;\r
983   return TRUE;\r
984 }\r
985 \r
986 \r
987 /* Set by InitInstance, used by EnsureOnScreen */\r
988 int screenHeight, screenWidth;\r
989 RECT screenGeometry;\r
990 \r
991 void\r
992 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
993 {\r
994 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
995   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
996   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
997   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
998   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
999   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1000 }\r
1001 \r
1002 VOID\r
1003 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1004 {\r
1005   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1006   GetCurrentDirectory(MSG_SIZ, dir);\r
1007   SetCurrentDirectory(installDir);\r
1008   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1009       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1010 \r
1011       if (cps->programLogo == NULL && appData.debugMode) {\r
1012           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1013       }\r
1014   } else if(appData.autoLogo) {\r
1015       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1016         char *opponent = "";\r
1017         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1018         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1019         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1020         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1021             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1022             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023         }\r
1024       } else\r
1025       if(appData.directory[n] && appData.directory[n][0]) {\r
1026         SetCurrentDirectory(appData.directory[n]);\r
1027         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1028       }\r
1029   }\r
1030   SetCurrentDirectory(dir); /* return to prev directory */\r
1031 }\r
1032 \r
1033 VOID\r
1034 InitTextures()\r
1035 {\r
1036   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1037   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1038   \r
1039   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1040       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1041       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1042       liteBackTextureMode = appData.liteBackTextureMode;\r
1043 \r
1044       if (liteBackTexture == NULL && appData.debugMode) {\r
1045           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1046       }\r
1047   }\r
1048   \r
1049   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1050       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1051       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1052       darkBackTextureMode = appData.darkBackTextureMode;\r
1053 \r
1054       if (darkBackTexture == NULL && appData.debugMode) {\r
1055           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1056       }\r
1057   }\r
1058 }\r
1059 \r
1060 #ifndef SM_CXVIRTUALSCREEN\r
1061 #define SM_CXVIRTUALSCREEN 78\r
1062 #endif\r
1063 #ifndef SM_CYVIRTUALSCREEN\r
1064 #define SM_CYVIRTUALSCREEN 79\r
1065 #endif\r
1066 #ifndef SM_XVIRTUALSCREEN \r
1067 #define SM_XVIRTUALSCREEN 76\r
1068 #endif\r
1069 #ifndef SM_YVIRTUALSCREEN \r
1070 #define SM_YVIRTUALSCREEN 77\r
1071 #endif\r
1072 \r
1073 VOID\r
1074 InitGeometry()\r
1075 {\r
1076   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1077   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1078   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1079   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1080   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1081   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1082   screenGeometry.right = screenGeometry.left + screenWidth;\r
1083   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1084 }\r
1085 \r
1086 BOOL\r
1087 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1088 {\r
1089   HWND hwnd; /* Main window handle. */\r
1090   int ibs;\r
1091   WINDOWPLACEMENT wp;\r
1092   char *filepart;\r
1093 \r
1094   hInst = hInstance;    /* Store instance handle in our global variable */\r
1095   programName = szAppName;\r
1096 \r
1097   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1098     *filepart = NULLCHAR;\r
1099     SetCurrentDirectory(installDir);\r
1100   } else {\r
1101     GetCurrentDirectory(MSG_SIZ, installDir);\r
1102   }\r
1103   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1104   InitGeometry();\r
1105   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1106   /* xboard, and older WinBoards, controlled the move sound with the\r
1107      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1108      always turn the option on (so that the backend will call us),\r
1109      then let the user turn the sound off by setting it to silence if\r
1110      desired.  To accommodate old winboard.ini files saved by old\r
1111      versions of WinBoard, we also turn off the sound if the option\r
1112      was initially set to false. [HGM] taken out of InitAppData */\r
1113   if (!appData.ringBellAfterMoves) {\r
1114     sounds[(int)SoundMove].name = strdup("");\r
1115     appData.ringBellAfterMoves = TRUE;\r
1116   }\r
1117   if (appData.debugMode) {\r
1118     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1119     setbuf(debugFP, NULL);\r
1120   }\r
1121 \r
1122   LoadLanguageFile(appData.language);\r
1123 \r
1124   InitBackEnd1();\r
1125 \r
1126 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1127 //  InitEngineUCI( installDir, &second );\r
1128 \r
1129   /* Create a main window for this application instance. */\r
1130   hwnd = CreateWindow(szAppName, szTitle,\r
1131                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1132                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1133                       NULL, NULL, hInstance, NULL);\r
1134   hwndMain = hwnd;\r
1135 \r
1136   /* If window could not be created, return "failure" */\r
1137   if (!hwnd) {\r
1138     return (FALSE);\r
1139   }\r
1140 \r
1141   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1142   LoadLogo(&first, 0, FALSE);\r
1143   LoadLogo(&second, 1, appData.icsActive);\r
1144 \r
1145   SetUserLogo();\r
1146 \r
1147   iconWhite = LoadIcon(hInstance, "icon_white");\r
1148   iconBlack = LoadIcon(hInstance, "icon_black");\r
1149   iconCurrent = iconWhite;\r
1150   InitDrawingColors();\r
1151 \r
1152   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1153   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1154     /* Compute window size for each board size, and use the largest\r
1155        size that fits on this screen as the default. */\r
1156     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1157     if (boardSize == (BoardSize)-1 &&\r
1158         winH <= screenHeight\r
1159            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1160         && winW <= screenWidth) {\r
1161       boardSize = (BoardSize)ibs;\r
1162     }\r
1163   }\r
1164 \r
1165   InitDrawingSizes(boardSize, 0);\r
1166   RecentEngineMenu(appData.recentEngineList);\r
1167   InitMenuChecks();\r
1168   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1169 \r
1170   /* [AS] Load textures if specified */\r
1171   InitTextures();\r
1172 \r
1173   mysrandom( (unsigned) time(NULL) );\r
1174 \r
1175   /* [AS] Restore layout */\r
1176   if( wpMoveHistory.visible ) {\r
1177       MoveHistoryPopUp();\r
1178   }\r
1179 \r
1180   if( wpEvalGraph.visible ) {\r
1181       EvalGraphPopUp();\r
1182   }\r
1183 \r
1184   if( wpEngineOutput.visible ) {\r
1185       EngineOutputPopUp();\r
1186   }\r
1187 \r
1188   /* Make the window visible; update its client area; and return "success" */\r
1189   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1190   wp.length = sizeof(WINDOWPLACEMENT);\r
1191   wp.flags = 0;\r
1192   wp.showCmd = nCmdShow;\r
1193   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1194   wp.rcNormalPosition.left = wpMain.x;\r
1195   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1196   wp.rcNormalPosition.top = wpMain.y;\r
1197   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1198   SetWindowPlacement(hwndMain, &wp);\r
1199 \r
1200   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1201 \r
1202   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1203                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1204 \r
1205   if (hwndConsole) {\r
1206 #if AOT_CONSOLE\r
1207     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1208                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1209 #endif\r
1210     ShowWindow(hwndConsole, nCmdShow);\r
1211     SetActiveWindow(hwndConsole);\r
1212   }\r
1213   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1214   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1215 \r
1216   return TRUE;\r
1217 \r
1218 }\r
1219 \r
1220 VOID\r
1221 InitMenuChecks()\r
1222 {\r
1223   HMENU hmenu = GetMenu(hwndMain);\r
1224 \r
1225   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1226                         MF_BYCOMMAND|((appData.icsActive &&\r
1227                                        *appData.icsCommPort != NULLCHAR) ?\r
1228                                       MF_ENABLED : MF_GRAYED));\r
1229   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1230                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1231                                      MF_CHECKED : MF_UNCHECKED));\r
1232 }\r
1233 \r
1234 //---------------------------------------------------------------------------------------------------------\r
1235 \r
1236 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1237 #define XBOARD FALSE\r
1238 \r
1239 #define OPTCHAR "/"\r
1240 #define SEPCHAR "="\r
1241 #define TOPLEVEL 0\r
1242 \r
1243 #include "args.h"\r
1244 \r
1245 // front-end part of option handling\r
1246 \r
1247 VOID\r
1248 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1249 {\r
1250   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1251   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1252   DeleteDC(hdc);\r
1253   lf->lfWidth = 0;\r
1254   lf->lfEscapement = 0;\r
1255   lf->lfOrientation = 0;\r
1256   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1257   lf->lfItalic = mfp->italic;\r
1258   lf->lfUnderline = mfp->underline;\r
1259   lf->lfStrikeOut = mfp->strikeout;\r
1260   lf->lfCharSet = mfp->charset;\r
1261   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1262   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1263   lf->lfQuality = DEFAULT_QUALITY;\r
1264   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1265     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1266 }\r
1267 \r
1268 void\r
1269 CreateFontInMF(MyFont *mf)\r
1270\r
1271   LFfromMFP(&mf->lf, &mf->mfp);\r
1272   if (mf->hf) DeleteObject(mf->hf);\r
1273   mf->hf = CreateFontIndirect(&mf->lf);\r
1274 }\r
1275 \r
1276 // [HGM] This platform-dependent table provides the location for storing the color info\r
1277 void *\r
1278 colorVariable[] = {\r
1279   &whitePieceColor, \r
1280   &blackPieceColor, \r
1281   &lightSquareColor,\r
1282   &darkSquareColor, \r
1283   &highlightSquareColor,\r
1284   &premoveHighlightColor,\r
1285   NULL,\r
1286   &consoleBackgroundColor,\r
1287   &appData.fontForeColorWhite,\r
1288   &appData.fontBackColorWhite,\r
1289   &appData.fontForeColorBlack,\r
1290   &appData.fontBackColorBlack,\r
1291   &appData.evalHistColorWhite,\r
1292   &appData.evalHistColorBlack,\r
1293   &appData.highlightArrowColor,\r
1294 };\r
1295 \r
1296 /* Command line font name parser.  NULL name means do nothing.\r
1297    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1298    For backward compatibility, syntax without the colon is also\r
1299    accepted, but font names with digits in them won't work in that case.\r
1300 */\r
1301 VOID\r
1302 ParseFontName(char *name, MyFontParams *mfp)\r
1303 {\r
1304   char *p, *q;\r
1305   if (name == NULL) return;\r
1306   p = name;\r
1307   q = strchr(p, ':');\r
1308   if (q) {\r
1309     if (q - p >= sizeof(mfp->faceName))\r
1310       ExitArgError(_("Font name too long:"), name, TRUE);\r
1311     memcpy(mfp->faceName, p, q - p);\r
1312     mfp->faceName[q - p] = NULLCHAR;\r
1313     p = q + 1;\r
1314   } else {\r
1315     q = mfp->faceName;\r
1316 \r
1317     while (*p && !isdigit(*p)) {\r
1318       *q++ = *p++;\r
1319       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1320         ExitArgError(_("Font name too long:"), name, TRUE);\r
1321     }\r
1322     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1323     *q = NULLCHAR;\r
1324   }\r
1325   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1326   mfp->pointSize = (float) atof(p);\r
1327   mfp->bold = (strchr(p, 'b') != NULL);\r
1328   mfp->italic = (strchr(p, 'i') != NULL);\r
1329   mfp->underline = (strchr(p, 'u') != NULL);\r
1330   mfp->strikeout = (strchr(p, 's') != NULL);\r
1331   mfp->charset = DEFAULT_CHARSET;\r
1332   q = strchr(p, 'c');\r
1333   if (q)\r
1334     mfp->charset = (BYTE) atoi(q+1);\r
1335 }\r
1336 \r
1337 void\r
1338 ParseFont(char *name, int number)\r
1339 { // wrapper to shield back-end from 'font'\r
1340   ParseFontName(name, &font[boardSize][number]->mfp);\r
1341 }\r
1342 \r
1343 void\r
1344 SetFontDefaults()\r
1345 { // in WB  we have a 2D array of fonts; this initializes their description\r
1346   int i, j;\r
1347   /* Point font array elements to structures and\r
1348      parse default font names */\r
1349   for (i=0; i<NUM_FONTS; i++) {\r
1350     for (j=0; j<NUM_SIZES; j++) {\r
1351       font[j][i] = &fontRec[j][i];\r
1352       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1353     }\r
1354   }\r
1355 }\r
1356 \r
1357 void\r
1358 CreateFonts()\r
1359 { // here we create the actual fonts from the selected descriptions\r
1360   int i, j;\r
1361   for (i=0; i<NUM_FONTS; i++) {\r
1362     for (j=0; j<NUM_SIZES; j++) {\r
1363       CreateFontInMF(font[j][i]);\r
1364     }\r
1365   }\r
1366 }\r
1367 /* Color name parser.\r
1368    X version accepts X color names, but this one\r
1369    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1370 COLORREF\r
1371 ParseColorName(char *name)\r
1372 {\r
1373   int red, green, blue, count;\r
1374   char buf[MSG_SIZ];\r
1375 \r
1376   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1377   if (count != 3) {\r
1378     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1379       &red, &green, &blue);\r
1380   }\r
1381   if (count != 3) {\r
1382     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1383     DisplayError(buf, 0);\r
1384     return RGB(0, 0, 0);\r
1385   }\r
1386   return PALETTERGB(red, green, blue);\r
1387 }\r
1388 \r
1389 void\r
1390 ParseColor(int n, char *name)\r
1391 { // for WinBoard the color is an int, which needs to be derived from the string\r
1392   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1393 }\r
1394 \r
1395 void\r
1396 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1397 {\r
1398   char *e = argValue;\r
1399   int eff = 0;\r
1400 \r
1401   while (*e) {\r
1402     if (*e == 'b')      eff |= CFE_BOLD;\r
1403     else if (*e == 'i') eff |= CFE_ITALIC;\r
1404     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1405     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1406     else if (*e == '#' || isdigit(*e)) break;\r
1407     e++;\r
1408   }\r
1409   *effects = eff;\r
1410   *color   = ParseColorName(e);\r
1411 }\r
1412 \r
1413 void\r
1414 ParseTextAttribs(ColorClass cc, char *s)\r
1415 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1416     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1417     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1418 }\r
1419 \r
1420 void\r
1421 ParseBoardSize(void *addr, char *name)\r
1422 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1423   BoardSize bs = SizeTiny;\r
1424   while (sizeInfo[bs].name != NULL) {\r
1425     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1426         *(BoardSize *)addr = bs;\r
1427         return;\r
1428     }\r
1429     bs++;\r
1430   }\r
1431   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1432 }\r
1433 \r
1434 void\r
1435 LoadAllSounds()\r
1436 { // [HGM] import name from appData first\r
1437   ColorClass cc;\r
1438   SoundClass sc;\r
1439   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1440     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1441     textAttribs[cc].sound.data = NULL;\r
1442     MyLoadSound(&textAttribs[cc].sound);\r
1443   }\r
1444   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1445     textAttribs[cc].sound.name = strdup("");\r
1446     textAttribs[cc].sound.data = NULL;\r
1447   }\r
1448   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1449     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1450     sounds[sc].data = NULL;\r
1451     MyLoadSound(&sounds[sc]);\r
1452   }\r
1453 }\r
1454 \r
1455 void\r
1456 SetCommPortDefaults()\r
1457 {\r
1458    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1459   dcb.DCBlength = sizeof(DCB);\r
1460   dcb.BaudRate = 9600;\r
1461   dcb.fBinary = TRUE;\r
1462   dcb.fParity = FALSE;\r
1463   dcb.fOutxCtsFlow = FALSE;\r
1464   dcb.fOutxDsrFlow = FALSE;\r
1465   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1466   dcb.fDsrSensitivity = FALSE;\r
1467   dcb.fTXContinueOnXoff = TRUE;\r
1468   dcb.fOutX = FALSE;\r
1469   dcb.fInX = FALSE;\r
1470   dcb.fNull = FALSE;\r
1471   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1472   dcb.fAbortOnError = FALSE;\r
1473   dcb.ByteSize = 7;\r
1474   dcb.Parity = SPACEPARITY;\r
1475   dcb.StopBits = ONESTOPBIT;\r
1476 }\r
1477 \r
1478 // [HGM] args: these three cases taken out to stay in front-end\r
1479 void\r
1480 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1481 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1482         // while the curent board size determines the element. This system should be ported to XBoard.\r
1483         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1484         int bs;\r
1485         for (bs=0; bs<NUM_SIZES; bs++) {\r
1486           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1487           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1488           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1489             ad->argName, mfp->faceName, mfp->pointSize,\r
1490             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1491             mfp->bold ? "b" : "",\r
1492             mfp->italic ? "i" : "",\r
1493             mfp->underline ? "u" : "",\r
1494             mfp->strikeout ? "s" : "",\r
1495             (int)mfp->charset);\r
1496         }\r
1497       }\r
1498 \r
1499 void\r
1500 ExportSounds()\r
1501 { // [HGM] copy the names from the internal WB variables to appData\r
1502   ColorClass cc;\r
1503   SoundClass sc;\r
1504   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1505     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1506   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1507     (&appData.soundMove)[sc] = sounds[sc].name;\r
1508 }\r
1509 \r
1510 void\r
1511 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1512 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1513         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1514         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1515           (ta->effects & CFE_BOLD) ? "b" : "",\r
1516           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1517           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1518           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1519           (ta->effects) ? " " : "",\r
1520           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1521       }\r
1522 \r
1523 void\r
1524 SaveColor(FILE *f, ArgDescriptor *ad)\r
1525 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1526         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1527         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1528           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1529 }\r
1530 \r
1531 void\r
1532 SaveBoardSize(FILE *f, char *name, void *addr)\r
1533 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1534   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1535 }\r
1536 \r
1537 void\r
1538 ParseCommPortSettings(char *s)\r
1539 { // wrapper to keep dcb from back-end\r
1540   ParseCommSettings(s, &dcb);\r
1541 }\r
1542 \r
1543 void\r
1544 GetWindowCoords()\r
1545 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1546   GetActualPlacement(hwndMain, &wpMain);\r
1547   GetActualPlacement(hwndConsole, &wpConsole);\r
1548   GetActualPlacement(commentDialog, &wpComment);\r
1549   GetActualPlacement(editTagsDialog, &wpTags);\r
1550   GetActualPlacement(gameListDialog, &wpGameList);\r
1551   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1552   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1553   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1554 }\r
1555 \r
1556 void\r
1557 PrintCommPortSettings(FILE *f, char *name)\r
1558 { // wrapper to shield back-end from DCB\r
1559       PrintCommSettings(f, name, &dcb);\r
1560 }\r
1561 \r
1562 int\r
1563 MySearchPath(char *installDir, char *name, char *fullname)\r
1564 {\r
1565   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1566   if(name[0]== '%') {\r
1567     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1568     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1569       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1570       *strchr(buf, '%') = 0;\r
1571       strcat(fullname, getenv(buf));\r
1572       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1573     }\r
1574     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1575     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1576     return (int) strlen(fullname);\r
1577   }\r
1578   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1579 }\r
1580 \r
1581 int\r
1582 MyGetFullPathName(char *name, char *fullname)\r
1583 {\r
1584   char *dummy;\r
1585   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1586 }\r
1587 \r
1588 int\r
1589 MainWindowUp()\r
1590 { // [HGM] args: allows testing if main window is realized from back-end\r
1591   return hwndMain != NULL;\r
1592 }\r
1593 \r
1594 void\r
1595 PopUpStartupDialog()\r
1596 {\r
1597     FARPROC lpProc;\r
1598     \r
1599     LoadLanguageFile(appData.language);\r
1600     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1601     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1602     FreeProcInstance(lpProc);\r
1603 }\r
1604 \r
1605 /*---------------------------------------------------------------------------*\\r
1606  *\r
1607  * GDI board drawing routines\r
1608  *\r
1609 \*---------------------------------------------------------------------------*/\r
1610 \r
1611 /* [AS] Draw square using background texture */\r
1612 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1613 {\r
1614     XFORM   x;\r
1615 \r
1616     if( mode == 0 ) {\r
1617         return; /* Should never happen! */\r
1618     }\r
1619 \r
1620     SetGraphicsMode( dst, GM_ADVANCED );\r
1621 \r
1622     switch( mode ) {\r
1623     case 1:\r
1624         /* Identity */\r
1625         break;\r
1626     case 2:\r
1627         /* X reflection */\r
1628         x.eM11 = -1.0;\r
1629         x.eM12 = 0;\r
1630         x.eM21 = 0;\r
1631         x.eM22 = 1.0;\r
1632         x.eDx = (FLOAT) dw + dx - 1;\r
1633         x.eDy = 0;\r
1634         dx = 0;\r
1635         SetWorldTransform( dst, &x );\r
1636         break;\r
1637     case 3:\r
1638         /* Y reflection */\r
1639         x.eM11 = 1.0;\r
1640         x.eM12 = 0;\r
1641         x.eM21 = 0;\r
1642         x.eM22 = -1.0;\r
1643         x.eDx = 0;\r
1644         x.eDy = (FLOAT) dh + dy - 1;\r
1645         dy = 0;\r
1646         SetWorldTransform( dst, &x );\r
1647         break;\r
1648     case 4:\r
1649         /* X/Y flip */\r
1650         x.eM11 = 0;\r
1651         x.eM12 = 1.0;\r
1652         x.eM21 = 1.0;\r
1653         x.eM22 = 0;\r
1654         x.eDx = (FLOAT) dx;\r
1655         x.eDy = (FLOAT) dy;\r
1656         dx = 0;\r
1657         dy = 0;\r
1658         SetWorldTransform( dst, &x );\r
1659         break;\r
1660     }\r
1661 \r
1662     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1663 \r
1664     x.eM11 = 1.0;\r
1665     x.eM12 = 0;\r
1666     x.eM21 = 0;\r
1667     x.eM22 = 1.0;\r
1668     x.eDx = 0;\r
1669     x.eDy = 0;\r
1670     SetWorldTransform( dst, &x );\r
1671 \r
1672     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1673 }\r
1674 \r
1675 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1676 enum {\r
1677     PM_WP = (int) WhitePawn, \r
1678     PM_WN = (int) WhiteKnight, \r
1679     PM_WB = (int) WhiteBishop, \r
1680     PM_WR = (int) WhiteRook, \r
1681     PM_WQ = (int) WhiteQueen, \r
1682     PM_WF = (int) WhiteFerz, \r
1683     PM_WW = (int) WhiteWazir, \r
1684     PM_WE = (int) WhiteAlfil, \r
1685     PM_WM = (int) WhiteMan, \r
1686     PM_WO = (int) WhiteCannon, \r
1687     PM_WU = (int) WhiteUnicorn, \r
1688     PM_WH = (int) WhiteNightrider, \r
1689     PM_WA = (int) WhiteAngel, \r
1690     PM_WC = (int) WhiteMarshall, \r
1691     PM_WAB = (int) WhiteCardinal, \r
1692     PM_WD = (int) WhiteDragon, \r
1693     PM_WL = (int) WhiteLance, \r
1694     PM_WS = (int) WhiteCobra, \r
1695     PM_WV = (int) WhiteFalcon, \r
1696     PM_WSG = (int) WhiteSilver, \r
1697     PM_WG = (int) WhiteGrasshopper, \r
1698     PM_WK = (int) WhiteKing,\r
1699     PM_BP = (int) BlackPawn, \r
1700     PM_BN = (int) BlackKnight, \r
1701     PM_BB = (int) BlackBishop, \r
1702     PM_BR = (int) BlackRook, \r
1703     PM_BQ = (int) BlackQueen, \r
1704     PM_BF = (int) BlackFerz, \r
1705     PM_BW = (int) BlackWazir, \r
1706     PM_BE = (int) BlackAlfil, \r
1707     PM_BM = (int) BlackMan,\r
1708     PM_BO = (int) BlackCannon, \r
1709     PM_BU = (int) BlackUnicorn, \r
1710     PM_BH = (int) BlackNightrider, \r
1711     PM_BA = (int) BlackAngel, \r
1712     PM_BC = (int) BlackMarshall, \r
1713     PM_BG = (int) BlackGrasshopper, \r
1714     PM_BAB = (int) BlackCardinal,\r
1715     PM_BD = (int) BlackDragon,\r
1716     PM_BL = (int) BlackLance,\r
1717     PM_BS = (int) BlackCobra,\r
1718     PM_BV = (int) BlackFalcon,\r
1719     PM_BSG = (int) BlackSilver,\r
1720     PM_BK = (int) BlackKing\r
1721 };\r
1722 \r
1723 static HFONT hPieceFont = NULL;\r
1724 static HBITMAP hPieceMask[(int) EmptySquare];\r
1725 static HBITMAP hPieceFace[(int) EmptySquare];\r
1726 static int fontBitmapSquareSize = 0;\r
1727 static char pieceToFontChar[(int) EmptySquare] =\r
1728                               { 'p', 'n', 'b', 'r', 'q', \r
1729                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1730                       'k', 'o', 'm', 'v', 't', 'w', \r
1731                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1732                                                               'l' };\r
1733 \r
1734 extern BOOL SetCharTable( char *table, const char * map );\r
1735 /* [HGM] moved to backend.c */\r
1736 \r
1737 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1738 {\r
1739     HBRUSH hbrush;\r
1740     BYTE r1 = GetRValue( color );\r
1741     BYTE g1 = GetGValue( color );\r
1742     BYTE b1 = GetBValue( color );\r
1743     BYTE r2 = r1 / 2;\r
1744     BYTE g2 = g1 / 2;\r
1745     BYTE b2 = b1 / 2;\r
1746     RECT rc;\r
1747 \r
1748     /* Create a uniform background first */\r
1749     hbrush = CreateSolidBrush( color );\r
1750     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1751     FillRect( hdc, &rc, hbrush );\r
1752     DeleteObject( hbrush );\r
1753     \r
1754     if( mode == 1 ) {\r
1755         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1756         int steps = squareSize / 2;\r
1757         int i;\r
1758 \r
1759         for( i=0; i<steps; i++ ) {\r
1760             BYTE r = r1 - (r1-r2) * i / steps;\r
1761             BYTE g = g1 - (g1-g2) * i / steps;\r
1762             BYTE b = b1 - (b1-b2) * i / steps;\r
1763 \r
1764             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1765             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1766             FillRect( hdc, &rc, hbrush );\r
1767             DeleteObject(hbrush);\r
1768         }\r
1769     }\r
1770     else if( mode == 2 ) {\r
1771         /* Diagonal gradient, good more or less for every piece */\r
1772         POINT triangle[3];\r
1773         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1774         HBRUSH hbrush_old;\r
1775         int steps = squareSize;\r
1776         int i;\r
1777 \r
1778         triangle[0].x = squareSize - steps;\r
1779         triangle[0].y = squareSize;\r
1780         triangle[1].x = squareSize;\r
1781         triangle[1].y = squareSize;\r
1782         triangle[2].x = squareSize;\r
1783         triangle[2].y = squareSize - steps;\r
1784 \r
1785         for( i=0; i<steps; i++ ) {\r
1786             BYTE r = r1 - (r1-r2) * i / steps;\r
1787             BYTE g = g1 - (g1-g2) * i / steps;\r
1788             BYTE b = b1 - (b1-b2) * i / steps;\r
1789 \r
1790             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1791             hbrush_old = SelectObject( hdc, hbrush );\r
1792             Polygon( hdc, triangle, 3 );\r
1793             SelectObject( hdc, hbrush_old );\r
1794             DeleteObject(hbrush);\r
1795             triangle[0].x++;\r
1796             triangle[2].y++;\r
1797         }\r
1798 \r
1799         SelectObject( hdc, hpen );\r
1800     }\r
1801 }\r
1802 \r
1803 /*\r
1804     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1805     seems to work ok. The main problem here is to find the "inside" of a chess\r
1806     piece: follow the steps as explained below.\r
1807 */\r
1808 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1809 {\r
1810     HBITMAP hbm;\r
1811     HBITMAP hbm_old;\r
1812     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1813     RECT rc;\r
1814     SIZE sz;\r
1815     POINT pt;\r
1816     int backColor = whitePieceColor; \r
1817     int foreColor = blackPieceColor;\r
1818     \r
1819     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1820         backColor = appData.fontBackColorWhite;\r
1821         foreColor = appData.fontForeColorWhite;\r
1822     }\r
1823     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1824         backColor = appData.fontBackColorBlack;\r
1825         foreColor = appData.fontForeColorBlack;\r
1826     }\r
1827 \r
1828     /* Mask */\r
1829     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831     hbm_old = SelectObject( hdc, hbm );\r
1832 \r
1833     rc.left = 0;\r
1834     rc.top = 0;\r
1835     rc.right = squareSize;\r
1836     rc.bottom = squareSize;\r
1837 \r
1838     /* Step 1: background is now black */\r
1839     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1840 \r
1841     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1842 \r
1843     pt.x = (squareSize - sz.cx) / 2;\r
1844     pt.y = (squareSize - sz.cy) / 2;\r
1845 \r
1846     SetBkMode( hdc, TRANSPARENT );\r
1847     SetTextColor( hdc, chroma );\r
1848     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1849     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1850 \r
1851     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1852     /* Step 3: the area outside the piece is filled with white */\r
1853 //    FloodFill( hdc, 0, 0, chroma );\r
1854     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1855     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1856     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1857     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1858     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1859     /* \r
1860         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1861         but if the start point is not inside the piece we're lost!\r
1862         There should be a better way to do this... if we could create a region or path\r
1863         from the fill operation we would be fine for example.\r
1864     */\r
1865 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1866     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1867 \r
1868     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1869         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1870         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1871 \r
1872         SelectObject( dc2, bm2 );\r
1873         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1874         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1877         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1878 \r
1879         DeleteDC( dc2 );\r
1880         DeleteObject( bm2 );\r
1881     }\r
1882 \r
1883     SetTextColor( hdc, 0 );\r
1884     /* \r
1885         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1886         draw the piece again in black for safety.\r
1887     */\r
1888     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1889 \r
1890     SelectObject( hdc, hbm_old );\r
1891 \r
1892     if( hPieceMask[index] != NULL ) {\r
1893         DeleteObject( hPieceMask[index] );\r
1894     }\r
1895 \r
1896     hPieceMask[index] = hbm;\r
1897 \r
1898     /* Face */\r
1899     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1900 \r
1901     SelectObject( hdc, hbm );\r
1902 \r
1903     {\r
1904         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1905         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1906         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1907 \r
1908         SelectObject( dc1, hPieceMask[index] );\r
1909         SelectObject( dc2, bm2 );\r
1910         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1911         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1912         \r
1913         /* \r
1914             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1915             the piece background and deletes (makes transparent) the rest.\r
1916             Thanks to that mask, we are free to paint the background with the greates\r
1917             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1918             We use this, to make gradients and give the pieces a "roundish" look.\r
1919         */\r
1920         SetPieceBackground( hdc, backColor, 2 );\r
1921         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1922 \r
1923         DeleteDC( dc2 );\r
1924         DeleteDC( dc1 );\r
1925         DeleteObject( bm2 );\r
1926     }\r
1927 \r
1928     SetTextColor( hdc, foreColor );\r
1929     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1930 \r
1931     SelectObject( hdc, hbm_old );\r
1932 \r
1933     if( hPieceFace[index] != NULL ) {\r
1934         DeleteObject( hPieceFace[index] );\r
1935     }\r
1936 \r
1937     hPieceFace[index] = hbm;\r
1938 }\r
1939 \r
1940 static int TranslatePieceToFontPiece( int piece )\r
1941 {\r
1942     switch( piece ) {\r
1943     case BlackPawn:\r
1944         return PM_BP;\r
1945     case BlackKnight:\r
1946         return PM_BN;\r
1947     case BlackBishop:\r
1948         return PM_BB;\r
1949     case BlackRook:\r
1950         return PM_BR;\r
1951     case BlackQueen:\r
1952         return PM_BQ;\r
1953     case BlackKing:\r
1954         return PM_BK;\r
1955     case WhitePawn:\r
1956         return PM_WP;\r
1957     case WhiteKnight:\r
1958         return PM_WN;\r
1959     case WhiteBishop:\r
1960         return PM_WB;\r
1961     case WhiteRook:\r
1962         return PM_WR;\r
1963     case WhiteQueen:\r
1964         return PM_WQ;\r
1965     case WhiteKing:\r
1966         return PM_WK;\r
1967 \r
1968     case BlackAngel:\r
1969         return PM_BA;\r
1970     case BlackMarshall:\r
1971         return PM_BC;\r
1972     case BlackFerz:\r
1973         return PM_BF;\r
1974     case BlackNightrider:\r
1975         return PM_BH;\r
1976     case BlackAlfil:\r
1977         return PM_BE;\r
1978     case BlackWazir:\r
1979         return PM_BW;\r
1980     case BlackUnicorn:\r
1981         return PM_BU;\r
1982     case BlackCannon:\r
1983         return PM_BO;\r
1984     case BlackGrasshopper:\r
1985         return PM_BG;\r
1986     case BlackMan:\r
1987         return PM_BM;\r
1988     case BlackSilver:\r
1989         return PM_BSG;\r
1990     case BlackLance:\r
1991         return PM_BL;\r
1992     case BlackFalcon:\r
1993         return PM_BV;\r
1994     case BlackCobra:\r
1995         return PM_BS;\r
1996     case BlackCardinal:\r
1997         return PM_BAB;\r
1998     case BlackDragon:\r
1999         return PM_BD;\r
2000 \r
2001     case WhiteAngel:\r
2002         return PM_WA;\r
2003     case WhiteMarshall:\r
2004         return PM_WC;\r
2005     case WhiteFerz:\r
2006         return PM_WF;\r
2007     case WhiteNightrider:\r
2008         return PM_WH;\r
2009     case WhiteAlfil:\r
2010         return PM_WE;\r
2011     case WhiteWazir:\r
2012         return PM_WW;\r
2013     case WhiteUnicorn:\r
2014         return PM_WU;\r
2015     case WhiteCannon:\r
2016         return PM_WO;\r
2017     case WhiteGrasshopper:\r
2018         return PM_WG;\r
2019     case WhiteMan:\r
2020         return PM_WM;\r
2021     case WhiteSilver:\r
2022         return PM_WSG;\r
2023     case WhiteLance:\r
2024         return PM_WL;\r
2025     case WhiteFalcon:\r
2026         return PM_WV;\r
2027     case WhiteCobra:\r
2028         return PM_WS;\r
2029     case WhiteCardinal:\r
2030         return PM_WAB;\r
2031     case WhiteDragon:\r
2032         return PM_WD;\r
2033     }\r
2034 \r
2035     return 0;\r
2036 }\r
2037 \r
2038 void CreatePiecesFromFont()\r
2039 {\r
2040     LOGFONT lf;\r
2041     HDC hdc_window = NULL;\r
2042     HDC hdc = NULL;\r
2043     HFONT hfont_old;\r
2044     int fontHeight;\r
2045     int i;\r
2046 \r
2047     if( fontBitmapSquareSize < 0 ) {\r
2048         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2049         return;\r
2050     }\r
2051 \r
2052     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2053             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2054         fontBitmapSquareSize = -1;\r
2055         return;\r
2056     }\r
2057 \r
2058     if( fontBitmapSquareSize != squareSize ) {\r
2059         hdc_window = GetDC( hwndMain );\r
2060         hdc = CreateCompatibleDC( hdc_window );\r
2061 \r
2062         if( hPieceFont != NULL ) {\r
2063             DeleteObject( hPieceFont );\r
2064         }\r
2065         else {\r
2066             for( i=0; i<=(int)BlackKing; i++ ) {\r
2067                 hPieceMask[i] = NULL;\r
2068                 hPieceFace[i] = NULL;\r
2069             }\r
2070         }\r
2071 \r
2072         fontHeight = 75;\r
2073 \r
2074         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2075             fontHeight = appData.fontPieceSize;\r
2076         }\r
2077 \r
2078         fontHeight = (fontHeight * squareSize) / 100;\r
2079 \r
2080         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2081         lf.lfWidth = 0;\r
2082         lf.lfEscapement = 0;\r
2083         lf.lfOrientation = 0;\r
2084         lf.lfWeight = FW_NORMAL;\r
2085         lf.lfItalic = 0;\r
2086         lf.lfUnderline = 0;\r
2087         lf.lfStrikeOut = 0;\r
2088         lf.lfCharSet = DEFAULT_CHARSET;\r
2089         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2090         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2091         lf.lfQuality = PROOF_QUALITY;\r
2092         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2093         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2094         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2095 \r
2096         hPieceFont = CreateFontIndirect( &lf );\r
2097 \r
2098         if( hPieceFont == NULL ) {\r
2099             fontBitmapSquareSize = -2;\r
2100         }\r
2101         else {\r
2102             /* Setup font-to-piece character table */\r
2103             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2104                 /* No (or wrong) global settings, try to detect the font */\r
2105                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2106                     /* Alpha */\r
2107                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2108                 }\r
2109                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2110                     /* DiagramTT* family */\r
2111                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2112                 }\r
2113                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2114                     /* Fairy symbols */\r
2115                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2116                 }\r
2117                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2118                     /* Good Companion (Some characters get warped as literal :-( */\r
2119                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2120                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2121                     SetCharTable(pieceToFontChar, s);\r
2122                 }\r
2123                 else {\r
2124                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2125                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2126                 }\r
2127             }\r
2128 \r
2129             /* Create bitmaps */\r
2130             hfont_old = SelectObject( hdc, hPieceFont );\r
2131             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2132                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2133                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2134 \r
2135             SelectObject( hdc, hfont_old );\r
2136 \r
2137             fontBitmapSquareSize = squareSize;\r
2138         }\r
2139     }\r
2140 \r
2141     if( hdc != NULL ) {\r
2142         DeleteDC( hdc );\r
2143     }\r
2144 \r
2145     if( hdc_window != NULL ) {\r
2146         ReleaseDC( hwndMain, hdc_window );\r
2147     }\r
2148 }\r
2149 \r
2150 HBITMAP\r
2151 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2152 {\r
2153   char name[128], buf[MSG_SIZ];\r
2154 \r
2155     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2156   if(appData.pieceDirectory[0]) {\r
2157     HBITMAP res;\r
2158     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2159     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2160     if(res) return res;\r
2161   }\r
2162   if (gameInfo.event &&\r
2163       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2164       strcmp(name, "k80s") == 0) {\r
2165     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2166   }\r
2167   return LoadBitmap(hinst, name);\r
2168 }\r
2169 \r
2170 \r
2171 /* Insert a color into the program's logical palette\r
2172    structure.  This code assumes the given color is\r
2173    the result of the RGB or PALETTERGB macro, and it\r
2174    knows how those macros work (which is documented).\r
2175 */\r
2176 VOID\r
2177 InsertInPalette(COLORREF color)\r
2178 {\r
2179   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2180 \r
2181   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2182     DisplayFatalError(_("Too many colors"), 0, 1);\r
2183     pLogPal->palNumEntries--;\r
2184     return;\r
2185   }\r
2186 \r
2187   pe->peFlags = (char) 0;\r
2188   pe->peRed = (char) (0xFF & color);\r
2189   pe->peGreen = (char) (0xFF & (color >> 8));\r
2190   pe->peBlue = (char) (0xFF & (color >> 16));\r
2191   return;\r
2192 }\r
2193 \r
2194 \r
2195 VOID\r
2196 InitDrawingColors()\r
2197 {\r
2198   if (pLogPal == NULL) {\r
2199     /* Allocate enough memory for a logical palette with\r
2200      * PALETTESIZE entries and set the size and version fields\r
2201      * of the logical palette structure.\r
2202      */\r
2203     pLogPal = (NPLOGPALETTE)\r
2204       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2205                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2206     pLogPal->palVersion    = 0x300;\r
2207   }\r
2208   pLogPal->palNumEntries = 0;\r
2209 \r
2210   InsertInPalette(lightSquareColor);\r
2211   InsertInPalette(darkSquareColor);\r
2212   InsertInPalette(whitePieceColor);\r
2213   InsertInPalette(blackPieceColor);\r
2214   InsertInPalette(highlightSquareColor);\r
2215   InsertInPalette(premoveHighlightColor);\r
2216 \r
2217   /*  create a logical color palette according the information\r
2218    *  in the LOGPALETTE structure.\r
2219    */\r
2220   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2221 \r
2222   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2223   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2224   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2225   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2226   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2227   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2228   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2229   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2230   /* [AS] Force rendering of the font-based pieces */\r
2231   if( fontBitmapSquareSize > 0 ) {\r
2232     fontBitmapSquareSize = 0;\r
2233   }\r
2234 }\r
2235 \r
2236 \r
2237 int\r
2238 BoardWidth(int boardSize, int n)\r
2239 { /* [HGM] argument n added to allow different width and height */\r
2240   int lineGap = sizeInfo[boardSize].lineGap;\r
2241 \r
2242   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2243       lineGap = appData.overrideLineGap;\r
2244   }\r
2245 \r
2246   return (n + 1) * lineGap +\r
2247           n * sizeInfo[boardSize].squareSize;\r
2248 }\r
2249 \r
2250 /* Respond to board resize by dragging edge */\r
2251 VOID\r
2252 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2253 {\r
2254   BoardSize newSize = NUM_SIZES - 1;\r
2255   static int recurse = 0;\r
2256   if (IsIconic(hwndMain)) return;\r
2257   if (recurse > 0) return;\r
2258   recurse++;\r
2259   while (newSize > 0) {\r
2260         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2261         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2262            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2263     newSize--;\r
2264   } \r
2265   boardSize = newSize;\r
2266   InitDrawingSizes(boardSize, flags);\r
2267   recurse--;\r
2268 }\r
2269 \r
2270 \r
2271 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2272 \r
2273 VOID\r
2274 InitDrawingSizes(BoardSize boardSize, int flags)\r
2275 {\r
2276   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2277   ChessSquare piece;\r
2278   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2279   HDC hdc;\r
2280   SIZE clockSize, messageSize;\r
2281   HFONT oldFont;\r
2282   char buf[MSG_SIZ];\r
2283   char *str;\r
2284   HMENU hmenu = GetMenu(hwndMain);\r
2285   RECT crect, wrect, oldRect;\r
2286   int offby;\r
2287   LOGBRUSH logbrush;\r
2288   VariantClass v = gameInfo.variant;\r
2289 \r
2290   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2291   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2292 \r
2293   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2294   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2295   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2296   oldBoardSize = boardSize;\r
2297 \r
2298   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2299   { // correct board size to one where built-in pieces exist\r
2300     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2301        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2302       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2303       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2304       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2305       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2306       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2307                                    boardSize = SizeMiddling;\r
2308     }\r
2309   }\r
2310   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2311 \r
2312   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2313   oldRect.top = wpMain.y;\r
2314   oldRect.right = wpMain.x + wpMain.width;\r
2315   oldRect.bottom = wpMain.y + wpMain.height;\r
2316 \r
2317   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2318   smallLayout = sizeInfo[boardSize].smallLayout;\r
2319   squareSize = sizeInfo[boardSize].squareSize;\r
2320   lineGap = sizeInfo[boardSize].lineGap;\r
2321   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2322   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2323 \r
2324   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2325       lineGap = appData.overrideLineGap;\r
2326   }\r
2327 \r
2328   if (tinyLayout != oldTinyLayout) {\r
2329     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2330     if (tinyLayout) {\r
2331       style &= ~WS_SYSMENU;\r
2332       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2333                  "&Minimize\tCtrl+F4");\r
2334     } else {\r
2335       style |= WS_SYSMENU;\r
2336       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2337     }\r
2338     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2339 \r
2340     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2341       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2342         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2343     }\r
2344     DrawMenuBar(hwndMain);\r
2345   }\r
2346 \r
2347   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2348   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2349 \r
2350   /* Get text area sizes */\r
2351   hdc = GetDC(hwndMain);\r
2352   if (appData.clockMode) {\r
2353     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2354   } else {\r
2355     snprintf(buf, MSG_SIZ, _("White"));\r
2356   }\r
2357   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2358   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2359   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2360   str = _("We only care about the height here");\r
2361   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2362   SelectObject(hdc, oldFont);\r
2363   ReleaseDC(hwndMain, hdc);\r
2364 \r
2365   /* Compute where everything goes */\r
2366   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2367         /* [HGM] logo: if either logo is on, reserve space for it */\r
2368         logoHeight =  2*clockSize.cy;\r
2369         leftLogoRect.left   = OUTER_MARGIN;\r
2370         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2371         leftLogoRect.top    = OUTER_MARGIN;\r
2372         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2373 \r
2374         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2375         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2376         rightLogoRect.top    = OUTER_MARGIN;\r
2377         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2378 \r
2379 \r
2380     whiteRect.left = leftLogoRect.right;\r
2381     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2382     whiteRect.top = OUTER_MARGIN;\r
2383     whiteRect.bottom = whiteRect.top + logoHeight;\r
2384 \r
2385     blackRect.right = rightLogoRect.left;\r
2386     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2387     blackRect.top = whiteRect.top;\r
2388     blackRect.bottom = whiteRect.bottom;\r
2389   } else {\r
2390     whiteRect.left = OUTER_MARGIN;\r
2391     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2392     whiteRect.top = OUTER_MARGIN;\r
2393     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2394 \r
2395     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2396     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2397     blackRect.top = whiteRect.top;\r
2398     blackRect.bottom = whiteRect.bottom;\r
2399 \r
2400     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2401   }\r
2402 \r
2403   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2404   if (appData.showButtonBar) {\r
2405     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2406       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2407   } else {\r
2408     messageRect.right = OUTER_MARGIN + boardWidth;\r
2409   }\r
2410   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2411   messageRect.bottom = messageRect.top + messageSize.cy;\r
2412 \r
2413   boardRect.left = OUTER_MARGIN;\r
2414   boardRect.right = boardRect.left + boardWidth;\r
2415   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2416   boardRect.bottom = boardRect.top + boardHeight;\r
2417 \r
2418   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2419   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2420   oldTinyLayout = tinyLayout;\r
2421   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2422   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2423     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2424   winW *= 1 + twoBoards;\r
2425   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2426   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2427   wpMain.height = winH; //       without disturbing window attachments\r
2428   GetWindowRect(hwndMain, &wrect);\r
2429   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2430                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2431 \r
2432   // [HGM] placement: let attached windows follow size change.\r
2433   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2434   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2435   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2436   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2438 \r
2439   /* compensate if menu bar wrapped */\r
2440   GetClientRect(hwndMain, &crect);\r
2441   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2442   wpMain.height += offby;\r
2443   switch (flags) {\r
2444   case WMSZ_TOPLEFT:\r
2445     SetWindowPos(hwndMain, NULL, \r
2446                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2447                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2448     break;\r
2449 \r
2450   case WMSZ_TOPRIGHT:\r
2451   case WMSZ_TOP:\r
2452     SetWindowPos(hwndMain, NULL, \r
2453                  wrect.left, wrect.bottom - wpMain.height, \r
2454                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2455     break;\r
2456 \r
2457   case WMSZ_BOTTOMLEFT:\r
2458   case WMSZ_LEFT:\r
2459     SetWindowPos(hwndMain, NULL, \r
2460                  wrect.right - wpMain.width, wrect.top, \r
2461                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2462     break;\r
2463 \r
2464   case WMSZ_BOTTOMRIGHT:\r
2465   case WMSZ_BOTTOM:\r
2466   case WMSZ_RIGHT:\r
2467   default:\r
2468     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2469                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2470     break;\r
2471   }\r
2472 \r
2473   hwndPause = NULL;\r
2474   for (i = 0; i < N_BUTTONS; i++) {\r
2475     if (buttonDesc[i].hwnd != NULL) {\r
2476       DestroyWindow(buttonDesc[i].hwnd);\r
2477       buttonDesc[i].hwnd = NULL;\r
2478     }\r
2479     if (appData.showButtonBar) {\r
2480       buttonDesc[i].hwnd =\r
2481         CreateWindow("BUTTON", buttonDesc[i].label,\r
2482                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2483                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2484                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2485                      (HMENU) buttonDesc[i].id,\r
2486                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2487       if (tinyLayout) {\r
2488         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2489                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2490                     MAKELPARAM(FALSE, 0));\r
2491       }\r
2492       if (buttonDesc[i].id == IDM_Pause)\r
2493         hwndPause = buttonDesc[i].hwnd;\r
2494       buttonDesc[i].wndproc = (WNDPROC)\r
2495         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2496     }\r
2497   }\r
2498   if (gridPen != NULL) DeleteObject(gridPen);\r
2499   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2500   if (premovePen != NULL) DeleteObject(premovePen);\r
2501   if (lineGap != 0) {\r
2502     logbrush.lbStyle = BS_SOLID;\r
2503     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2504     gridPen =\r
2505       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2506                    lineGap, &logbrush, 0, NULL);\r
2507     logbrush.lbColor = highlightSquareColor;\r
2508     highlightPen =\r
2509       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2510                    lineGap, &logbrush, 0, NULL);\r
2511 \r
2512     logbrush.lbColor = premoveHighlightColor; \r
2513     premovePen =\r
2514       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2515                    lineGap, &logbrush, 0, NULL);\r
2516 \r
2517     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2518     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2519       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2520       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2521         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2522       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2523         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2524       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2525     }\r
2526     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2527       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2528       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2529         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2530         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2531       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2532         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2533       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2534     }\r
2535   }\r
2536 \r
2537   /* [HGM] Licensing requirement */\r
2538 #ifdef GOTHIC\r
2539   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2540 #endif\r
2541 #ifdef FALCON\r
2542   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2543 #endif\r
2544   GothicPopUp( "", VariantNormal);\r
2545 \r
2546 \r
2547 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2548 \r
2549   /* Load piece bitmaps for this board size */\r
2550   for (i=0; i<=2; i++) {\r
2551     for (piece = WhitePawn;\r
2552          (int) piece < (int) BlackPawn;\r
2553          piece = (ChessSquare) ((int) piece + 1)) {\r
2554       if (pieceBitmap[i][piece] != NULL)\r
2555         DeleteObject(pieceBitmap[i][piece]);\r
2556     }\r
2557   }\r
2558 \r
2559   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2560   // Orthodox Chess pieces\r
2561   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2562   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2563   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2564   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2565   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2566   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2567   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2568   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2569   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2570   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2571   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2572   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2573   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2574   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2575   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2576   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2577     // in Shogi, Hijack the unused Queen for Lance\r
2578     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2579     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2580     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2581   } else {\r
2582     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2583     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2584     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2585   }\r
2586 \r
2587   if(squareSize <= 72 && squareSize >= 33) { \r
2588     /* A & C are available in most sizes now */\r
2589     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2590       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2591       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2592       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2593       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2594       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2595       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2596       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2597       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2598       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2599       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2600       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2601       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2602     } else { // Smirf-like\r
2603       if(gameInfo.variant == VariantSChess) {\r
2604         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2605         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2606         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2607       } else {\r
2608         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2609         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2610         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2611       }\r
2612     }\r
2613     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2614       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2615       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2616       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2617     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2618       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2619       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2620       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2621     } else { // WinBoard standard\r
2622       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2623       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2624       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2625     }\r
2626   }\r
2627 \r
2628 \r
2629   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2630     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2631     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2632     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2633     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2634     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2635     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2636     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2637     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2638     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2639     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2640     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2641     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2642     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2646     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2647     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2648     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2649     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2650     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2651     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2652     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2653     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2654     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2655     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2656     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2657     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2658     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2659     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2660 \r
2661     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2662       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2663       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2664       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2665       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2666       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2667       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2668       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2669       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2670       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2671       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2672       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2673       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2674     } else {\r
2675       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2676       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2677       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2678       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2679       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2680       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2681       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2682       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2683       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2684       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2685       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2686       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2687     }\r
2688 \r
2689   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2690     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2691     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2692     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2693     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2694     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2695     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2696     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2697     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2698     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2699     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2700     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2701     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2702     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2703     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2704   }\r
2705 \r
2706 \r
2707   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2708   /* special Shogi support in this size */\r
2709   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2710       for (piece = WhitePawn;\r
2711            (int) piece < (int) BlackPawn;\r
2712            piece = (ChessSquare) ((int) piece + 1)) {\r
2713         if (pieceBitmap[i][piece] != NULL)\r
2714           DeleteObject(pieceBitmap[i][piece]);\r
2715       }\r
2716     }\r
2717   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2718   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2719   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2720   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2721   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2722   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2723   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2724   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2725   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2726   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2727   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2728   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2729   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2730   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2731   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2732   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2733   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2734   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2735   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2736   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2737   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2738   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2739   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2740   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2741   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2742   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2743   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2744   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2745   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2746   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2747   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2748   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2749   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2750   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2751   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2752   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2753   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2754   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2755   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2756   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2757   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2758   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2759   minorSize = 0;\r
2760   }\r
2761 }\r
2762 \r
2763 HBITMAP\r
2764 PieceBitmap(ChessSquare p, int kind)\r
2765 {\r
2766   if ((int) p >= (int) BlackPawn)\r
2767     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2768 \r
2769   return pieceBitmap[kind][(int) p];\r
2770 }\r
2771 \r
2772 /***************************************************************/\r
2773 \r
2774 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2775 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2776 /*\r
2777 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2778 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2779 */\r
2780 \r
2781 VOID\r
2782 SquareToPos(int row, int column, int * x, int * y)\r
2783 {\r
2784   if (flipView) {\r
2785     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2786     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2787   } else {\r
2788     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2789     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2790   }\r
2791 }\r
2792 \r
2793 VOID\r
2794 DrawCoordsOnDC(HDC hdc)\r
2795 {\r
2796   static char files[] = "0123456789012345678901221098765432109876543210";\r
2797   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2798   char str[2] = { NULLCHAR, NULLCHAR };\r
2799   int oldMode, oldAlign, x, y, start, i;\r
2800   HFONT oldFont;\r
2801   HBRUSH oldBrush;\r
2802 \r
2803   if (!appData.showCoords)\r
2804     return;\r
2805 \r
2806   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2807 \r
2808   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2809   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2810   oldAlign = GetTextAlign(hdc);\r
2811   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2812 \r
2813   y = boardRect.top + lineGap;\r
2814   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2815 \r
2816   if(border) {\r
2817     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2818     x += border - lineGap - 4; y += squareSize - 6;\r
2819   } else\r
2820   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2821   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2822     str[0] = files[start + i];\r
2823     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2824     y += squareSize + lineGap;\r
2825   }\r
2826 \r
2827   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2828 \r
2829   if(border) {\r
2830     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2831     x += -border + 4; y += border - squareSize + 6;\r
2832   } else\r
2833   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2834   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2835     str[0] = ranks[start + i];\r
2836     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2837     x += squareSize + lineGap;\r
2838   }    \r
2839 \r
2840   SelectObject(hdc, oldBrush);\r
2841   SetBkMode(hdc, oldMode);\r
2842   SetTextAlign(hdc, oldAlign);\r
2843   SelectObject(hdc, oldFont);\r
2844 }\r
2845 \r
2846 VOID\r
2847 DrawGridOnDC(HDC hdc)\r
2848 {\r
2849   HPEN oldPen;\r
2850  \r
2851   if (lineGap != 0) {\r
2852     oldPen = SelectObject(hdc, gridPen);\r
2853     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2854     SelectObject(hdc, oldPen);\r
2855   }\r
2856 }\r
2857 \r
2858 #define HIGHLIGHT_PEN 0\r
2859 #define PREMOVE_PEN   1\r
2860 \r
2861 VOID\r
2862 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2863 {\r
2864   int x1, y1;\r
2865   HPEN oldPen, hPen;\r
2866   if (lineGap == 0) return;\r
2867   if (flipView) {\r
2868     x1 = boardRect.left +\r
2869       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2870     y1 = boardRect.top +\r
2871       lineGap/2 + y * (squareSize + lineGap) + border;\r
2872   } else {\r
2873     x1 = boardRect.left +\r
2874       lineGap/2 + x * (squareSize + lineGap) + border;\r
2875     y1 = boardRect.top +\r
2876       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2877   }\r
2878   hPen = pen ? premovePen : highlightPen;\r
2879   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2880   MoveToEx(hdc, x1, y1, NULL);\r
2881   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2882   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2883   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2884   LineTo(hdc, x1, y1);\r
2885   SelectObject(hdc, oldPen);\r
2886 }\r
2887 \r
2888 VOID\r
2889 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2890 {\r
2891   int i;\r
2892   for (i=0; i<2; i++) {\r
2893     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2894       DrawHighlightOnDC(hdc, TRUE,\r
2895                         h->sq[i].x, h->sq[i].y,\r
2896                         pen);\r
2897   }\r
2898 }\r
2899 \r
2900 /* Note: sqcolor is used only in monoMode */\r
2901 /* Note that this code is largely duplicated in woptions.c,\r
2902    function DrawSampleSquare, so that needs to be updated too */\r
2903 VOID\r
2904 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2905 {\r
2906   HBITMAP oldBitmap;\r
2907   HBRUSH oldBrush;\r
2908   int tmpSize;\r
2909 \r
2910   if (appData.blindfold) return;\r
2911 \r
2912   /* [AS] Use font-based pieces if needed */\r
2913   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2914     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2915     CreatePiecesFromFont();\r
2916 \r
2917     if( fontBitmapSquareSize == squareSize ) {\r
2918         int index = TranslatePieceToFontPiece(piece);\r
2919 \r
2920         SelectObject( tmphdc, hPieceMask[ index ] );\r
2921 \r
2922       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2923         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2924       else\r
2925         BitBlt( hdc,\r
2926             x, y,\r
2927             squareSize, squareSize,\r
2928             tmphdc,\r
2929             0, 0,\r
2930             SRCAND );\r
2931 \r
2932         SelectObject( tmphdc, hPieceFace[ index ] );\r
2933 \r
2934       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2935         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2936       else\r
2937         BitBlt( hdc,\r
2938             x, y,\r
2939             squareSize, squareSize,\r
2940             tmphdc,\r
2941             0, 0,\r
2942             SRCPAINT );\r
2943 \r
2944         return;\r
2945     }\r
2946   }\r
2947 \r
2948   if (appData.monoMode) {\r
2949     SelectObject(tmphdc, PieceBitmap(piece, \r
2950       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2951     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2952            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2953   } else {\r
2954     HBRUSH xBrush = whitePieceBrush;\r
2955     tmpSize = squareSize;\r
2956     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2957     if(minorSize &&\r
2958         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2959          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2960       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2961       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2962       x += (squareSize - minorSize)>>1;\r
2963       y += squareSize - minorSize - 2;\r
2964       tmpSize = minorSize;\r
2965     }\r
2966     if (color || appData.allWhite ) {\r
2967       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2968       if( color )\r
2969               oldBrush = SelectObject(hdc, xBrush);\r
2970       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2971       if(appData.upsideDown && color==flipView)\r
2972         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2973       else\r
2974         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2975       /* Use black for outline of white pieces */\r
2976       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2977       if(appData.upsideDown && color==flipView)\r
2978         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2979       else\r
2980         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2981     } else if(appData.pieceDirectory[0]) {\r
2982       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2983       oldBrush = SelectObject(hdc, xBrush);\r
2984       if(appData.upsideDown && color==flipView)\r
2985         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2986       else\r
2987         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2988       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2989       if(appData.upsideDown && color==flipView)\r
2990         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2991       else\r
2992         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2993     } else {\r
2994       /* Use square color for details of black pieces */\r
2995       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2996       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2997       if(appData.upsideDown && !flipView)\r
2998         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2999       else\r
3000         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3001     }\r
3002     SelectObject(hdc, oldBrush);\r
3003     SelectObject(tmphdc, oldBitmap);\r
3004   }\r
3005 }\r
3006 \r
3007 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3008 int GetBackTextureMode( int algo )\r
3009 {\r
3010     int result = BACK_TEXTURE_MODE_DISABLED;\r
3011 \r
3012     switch( algo ) \r
3013     {\r
3014         case BACK_TEXTURE_MODE_PLAIN:\r
3015             result = 1; /* Always use identity map */\r
3016             break;\r
3017         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3018             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3019             break;\r
3020     }\r
3021 \r
3022     return result;\r
3023 }\r
3024 \r
3025 /* \r
3026     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3027     to handle redraws cleanly (as random numbers would always be different).\r
3028 */\r
3029 VOID RebuildTextureSquareInfo()\r
3030 {\r
3031     BITMAP bi;\r
3032     int lite_w = 0;\r
3033     int lite_h = 0;\r
3034     int dark_w = 0;\r
3035     int dark_h = 0;\r
3036     int row;\r
3037     int col;\r
3038 \r
3039     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3040 \r
3041     if( liteBackTexture != NULL ) {\r
3042         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3043             lite_w = bi.bmWidth;\r
3044             lite_h = bi.bmHeight;\r
3045         }\r
3046     }\r
3047 \r
3048     if( darkBackTexture != NULL ) {\r
3049         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3050             dark_w = bi.bmWidth;\r
3051             dark_h = bi.bmHeight;\r
3052         }\r
3053     }\r
3054 \r
3055     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3056         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3057             if( (col + row) & 1 ) {\r
3058                 /* Lite square */\r
3059                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3060                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3061                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3062                   else\r
3063                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3064                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3065                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3066                   else\r
3067                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3068                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3069                 }\r
3070             }\r
3071             else {\r
3072                 /* Dark square */\r
3073                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3074                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3075                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3076                   else\r
3077                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3078                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3079                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3080                   else\r
3081                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3082                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3083                 }\r
3084             }\r
3085         }\r
3086     }\r
3087 }\r
3088 \r
3089 /* [AS] Arrow highlighting support */\r
3090 \r
3091 static double A_WIDTH = 5; /* Width of arrow body */\r
3092 \r
3093 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3094 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3095 \r
3096 static double Sqr( double x )\r
3097 {\r
3098     return x*x;\r
3099 }\r
3100 \r
3101 static int Round( double x )\r
3102 {\r
3103     return (int) (x + 0.5);\r
3104 }\r
3105 \r
3106 /* Draw an arrow between two points using current settings */\r
3107 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3108 {\r
3109     POINT arrow[7];\r
3110     double dx, dy, j, k, x, y;\r
3111 \r
3112     if( d_x == s_x ) {\r
3113         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3114 \r
3115         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3116         arrow[0].y = s_y;\r
3117 \r
3118         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3119         arrow[1].y = d_y - h;\r
3120 \r
3121         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3122         arrow[2].y = d_y - h;\r
3123 \r
3124         arrow[3].x = d_x;\r
3125         arrow[3].y = d_y;\r
3126 \r
3127         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3128         arrow[5].y = d_y - h;\r
3129 \r
3130         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3131         arrow[4].y = d_y - h;\r
3132 \r
3133         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3134         arrow[6].y = s_y;\r
3135     }\r
3136     else if( d_y == s_y ) {\r
3137         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3138 \r
3139         arrow[0].x = s_x;\r
3140         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3141 \r
3142         arrow[1].x = d_x - w;\r
3143         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3144 \r
3145         arrow[2].x = d_x - w;\r
3146         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3147 \r
3148         arrow[3].x = d_x;\r
3149         arrow[3].y = d_y;\r
3150 \r
3151         arrow[5].x = d_x - w;\r
3152         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3153 \r
3154         arrow[4].x = d_x - w;\r
3155         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3156 \r
3157         arrow[6].x = s_x;\r
3158         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3159     }\r
3160     else {\r
3161         /* [AS] Needed a lot of paper for this! :-) */\r
3162         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3163         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3164   \r
3165         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3166 \r
3167         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3168 \r
3169         x = s_x;\r
3170         y = s_y;\r
3171 \r
3172         arrow[0].x = Round(x - j);\r
3173         arrow[0].y = Round(y + j*dx);\r
3174 \r
3175         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3176         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3177 \r
3178         if( d_x > s_x ) {\r
3179             x = (double) d_x - k;\r
3180             y = (double) d_y - k*dy;\r
3181         }\r
3182         else {\r
3183             x = (double) d_x + k;\r
3184             y = (double) d_y + k*dy;\r
3185         }\r
3186 \r
3187         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3188 \r
3189         arrow[6].x = Round(x - j);\r
3190         arrow[6].y = Round(y + j*dx);\r
3191 \r
3192         arrow[2].x = Round(arrow[6].x + 2*j);\r
3193         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3194 \r
3195         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3196         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3197 \r
3198         arrow[4].x = d_x;\r
3199         arrow[4].y = d_y;\r
3200 \r
3201         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3202         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3203     }\r
3204 \r
3205     Polygon( hdc, arrow, 7 );\r
3206 }\r
3207 \r
3208 /* [AS] Draw an arrow between two squares */\r
3209 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3210 {\r
3211     int s_x, s_y, d_x, d_y;\r
3212     HPEN hpen;\r
3213     HPEN holdpen;\r
3214     HBRUSH hbrush;\r
3215     HBRUSH holdbrush;\r
3216     LOGBRUSH stLB;\r
3217 \r
3218     if( s_col == d_col && s_row == d_row ) {\r
3219         return;\r
3220     }\r
3221 \r
3222     /* Get source and destination points */\r
3223     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3224     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3225 \r
3226     if( d_y > s_y ) {\r
3227         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3228     }\r
3229     else if( d_y < s_y ) {\r
3230         d_y += squareSize / 2 + squareSize / 4;\r
3231     }\r
3232     else {\r
3233         d_y += squareSize / 2;\r
3234     }\r
3235 \r
3236     if( d_x > s_x ) {\r
3237         d_x += squareSize / 2 - squareSize / 4;\r
3238     }\r
3239     else if( d_x < s_x ) {\r
3240         d_x += squareSize / 2 + squareSize / 4;\r
3241     }\r
3242     else {\r
3243         d_x += squareSize / 2;\r
3244     }\r
3245 \r
3246     s_x += squareSize / 2;\r
3247     s_y += squareSize / 2;\r
3248 \r
3249     /* Adjust width */\r
3250     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3251 \r
3252     /* Draw */\r
3253     stLB.lbStyle = BS_SOLID;\r
3254     stLB.lbColor = appData.highlightArrowColor;\r
3255     stLB.lbHatch = 0;\r
3256 \r
3257     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3258     holdpen = SelectObject( hdc, hpen );\r
3259     hbrush = CreateBrushIndirect( &stLB );\r
3260     holdbrush = SelectObject( hdc, hbrush );\r
3261 \r
3262     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3263 \r
3264     SelectObject( hdc, holdpen );\r
3265     SelectObject( hdc, holdbrush );\r
3266     DeleteObject( hpen );\r
3267     DeleteObject( hbrush );\r
3268 }\r
3269 \r
3270 BOOL HasHighlightInfo()\r
3271 {\r
3272     BOOL result = FALSE;\r
3273 \r
3274     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3275         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3276     {\r
3277         result = TRUE;\r
3278     }\r
3279 \r
3280     return result;\r
3281 }\r
3282 \r
3283 BOOL IsDrawArrowEnabled()\r
3284 {\r
3285     BOOL result = FALSE;\r
3286 \r
3287     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3288         result = TRUE;\r
3289     }\r
3290 \r
3291     return result;\r
3292 }\r
3293 \r
3294 VOID DrawArrowHighlight( HDC hdc )\r
3295 {\r
3296     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3297         DrawArrowBetweenSquares( hdc,\r
3298             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3299             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3300     }\r
3301 }\r
3302 \r
3303 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3304 {\r
3305     HRGN result = NULL;\r
3306 \r
3307     if( HasHighlightInfo() ) {\r
3308         int x1, y1, x2, y2;\r
3309         int sx, sy, dx, dy;\r
3310 \r
3311         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3312         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3313 \r
3314         sx = MIN( x1, x2 );\r
3315         sy = MIN( y1, y2 );\r
3316         dx = MAX( x1, x2 ) + squareSize;\r
3317         dy = MAX( y1, y2 ) + squareSize;\r
3318 \r
3319         result = CreateRectRgn( sx, sy, dx, dy );\r
3320     }\r
3321 \r
3322     return result;\r
3323 }\r
3324 \r
3325 /*\r
3326     Warning: this function modifies the behavior of several other functions. \r
3327     \r
3328     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3329     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3330     repaint is scattered all over the place, which is not good for features such as\r
3331     "arrow highlighting" that require a full repaint of the board.\r
3332 \r
3333     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3334     user interaction, when speed is not so important) but especially to avoid errors\r
3335     in the displayed graphics.\r
3336 \r
3337     In such patched places, I always try refer to this function so there is a single\r
3338     place to maintain knowledge.\r
3339     \r
3340     To restore the original behavior, just return FALSE unconditionally.\r
3341 */\r
3342 BOOL IsFullRepaintPreferrable()\r
3343 {\r
3344     BOOL result = FALSE;\r
3345 \r
3346     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3347         /* Arrow may appear on the board */\r
3348         result = TRUE;\r
3349     }\r
3350 \r
3351     return result;\r
3352 }\r
3353 \r
3354 /* \r
3355     This function is called by DrawPosition to know whether a full repaint must\r
3356     be forced or not.\r
3357 \r
3358     Only DrawPosition may directly call this function, which makes use of \r
3359     some state information. Other function should call DrawPosition specifying \r
3360     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3361 */\r
3362 BOOL DrawPositionNeedsFullRepaint()\r
3363 {\r
3364     BOOL result = FALSE;\r
3365 \r
3366     /* \r
3367         Probably a slightly better policy would be to trigger a full repaint\r
3368         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3369         but animation is fast enough that it's difficult to notice.\r
3370     */\r
3371     if( animInfo.piece == EmptySquare ) {\r
3372         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3373             result = TRUE;\r
3374         }\r
3375     }\r
3376 \r
3377     return result;\r
3378 }\r
3379 \r
3380 static HBITMAP borderBitmap;\r
3381 \r
3382 VOID\r
3383 DrawBackgroundOnDC(HDC hdc)\r
3384 {\r
3385   \r
3386   BITMAP bi;\r
3387   HDC tmphdc;\r
3388   HBITMAP hbm;\r
3389   static char oldBorder[MSG_SIZ];\r
3390   int w = 600, h = 600, mode;\r
3391 \r
3392   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3393     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3394     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3395   }\r
3396   if(borderBitmap == NULL) { // loading failed, use white\r
3397     FillRect( hdc, &boardRect, whitePieceBrush );\r
3398     return;\r
3399   }\r
3400   tmphdc = CreateCompatibleDC(hdc);\r
3401   hbm = SelectObject(tmphdc, borderBitmap);\r
3402   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3403             w = bi.bmWidth;\r
3404             h = bi.bmHeight;\r
3405   }\r
3406   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3407   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3408                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3409   SetStretchBltMode(hdc, mode);\r
3410   SelectObject(tmphdc, hbm);\r
3411   DeleteDC(tmphdc);\r
3412 }\r
3413 \r
3414 VOID\r
3415 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3416 {\r
3417   int row, column, x, y, square_color, piece_color;\r
3418   ChessSquare piece;\r
3419   HBRUSH oldBrush;\r
3420   HDC texture_hdc = NULL;\r
3421 \r
3422   /* [AS] Initialize background textures if needed */\r
3423   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3424       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3425       if( backTextureSquareSize != squareSize \r
3426        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3427           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3428           backTextureSquareSize = squareSize;\r
3429           RebuildTextureSquareInfo();\r
3430       }\r
3431 \r
3432       texture_hdc = CreateCompatibleDC( hdc );\r
3433   }\r
3434 \r
3435   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3436     for (column = 0; column < BOARD_WIDTH; column++) {\r
3437   \r
3438       SquareToPos(row, column, &x, &y);\r
3439 \r
3440       piece = board[row][column];\r
3441 \r
3442       square_color = ((column + row) % 2) == 1;\r
3443       if( gameInfo.variant == VariantXiangqi ) {\r
3444           square_color = !InPalace(row, column);\r
3445           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3446           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3447       }\r
3448       piece_color = (int) piece < (int) BlackPawn;\r
3449 \r
3450 \r
3451       /* [HGM] holdings file: light square or black */\r
3452       if(column == BOARD_LEFT-2) {\r
3453             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3454                 square_color = 1;\r
3455             else {\r
3456                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3457                 continue;\r
3458             }\r
3459       } else\r
3460       if(column == BOARD_RGHT + 1 ) {\r
3461             if( row < gameInfo.holdingsSize )\r
3462                 square_color = 1;\r
3463             else {\r
3464                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3465                 continue;\r
3466             }\r
3467       }\r
3468       if(column == BOARD_LEFT-1 ) /* left align */\r
3469             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3470       else if( column == BOARD_RGHT) /* right align */\r
3471             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3472       else\r
3473       if (appData.monoMode) {\r
3474         if (piece == EmptySquare) {\r
3475           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3476                  square_color ? WHITENESS : BLACKNESS);\r
3477         } else {\r
3478           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3479         }\r
3480       } \r
3481       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3482           /* [AS] Draw the square using a texture bitmap */\r
3483           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3484           int r = row, c = column; // [HGM] do not flip board in flipView\r
3485           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3486 \r
3487           DrawTile( x, y, \r
3488               squareSize, squareSize, \r
3489               hdc, \r
3490               texture_hdc,\r
3491               backTextureSquareInfo[r][c].mode,\r
3492               backTextureSquareInfo[r][c].x,\r
3493               backTextureSquareInfo[r][c].y );\r
3494 \r
3495           SelectObject( texture_hdc, hbm );\r
3496 \r
3497           if (piece != EmptySquare) {\r
3498               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3499           }\r
3500       }\r
3501       else {\r
3502         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3503 \r
3504         oldBrush = SelectObject(hdc, brush );\r
3505         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3506         SelectObject(hdc, oldBrush);\r
3507         if (piece != EmptySquare)\r
3508           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3509       }\r
3510     }\r
3511   }\r
3512 \r
3513   if( texture_hdc != NULL ) {\r
3514     DeleteDC( texture_hdc );\r
3515   }\r
3516 }\r
3517 \r
3518 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3519 void fputDW(FILE *f, int x)\r
3520 {\r
3521         fputc(x     & 255, f);\r
3522         fputc(x>>8  & 255, f);\r
3523         fputc(x>>16 & 255, f);\r
3524         fputc(x>>24 & 255, f);\r
3525 }\r
3526 \r
3527 #define MAX_CLIPS 200   /* more than enough */\r
3528 \r
3529 VOID\r
3530 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3531 {\r
3532 //  HBITMAP bufferBitmap;\r
3533   BITMAP bi;\r
3534 //  RECT Rect;\r
3535   HDC tmphdc;\r
3536   HBITMAP hbm;\r
3537   int w = 100, h = 50;\r
3538 \r
3539   if(logo == NULL) {\r
3540     if(!logoHeight) return;\r
3541     FillRect( hdc, &logoRect, whitePieceBrush );\r
3542   }\r
3543 //  GetClientRect(hwndMain, &Rect);\r
3544 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3545 //                                      Rect.bottom-Rect.top+1);\r
3546   tmphdc = CreateCompatibleDC(hdc);\r
3547   hbm = SelectObject(tmphdc, logo);\r
3548   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3549             w = bi.bmWidth;\r
3550             h = bi.bmHeight;\r
3551   }\r
3552   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3553                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3554   SelectObject(tmphdc, hbm);\r
3555   DeleteDC(tmphdc);\r
3556 }\r
3557 \r
3558 VOID\r
3559 DisplayLogos()\r
3560 {\r
3561   if(logoHeight) {\r
3562         HDC hdc = GetDC(hwndMain);\r
3563         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3564         if(appData.autoLogo) {\r
3565           \r
3566           switch(gameMode) { // pick logos based on game mode\r
3567             case IcsObserving:\r
3568                 whiteLogo = second.programLogo; // ICS logo\r
3569                 blackLogo = second.programLogo;\r
3570             default:\r
3571                 break;\r
3572             case IcsPlayingWhite:\r
3573                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3574                 blackLogo = second.programLogo; // ICS logo\r
3575                 break;\r
3576             case IcsPlayingBlack:\r
3577                 whiteLogo = second.programLogo; // ICS logo\r
3578                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3579                 break;\r
3580             case TwoMachinesPlay:\r
3581                 if(first.twoMachinesColor[0] == 'b') {\r
3582                     whiteLogo = second.programLogo;\r
3583                     blackLogo = first.programLogo;\r
3584                 }\r
3585                 break;\r
3586             case MachinePlaysWhite:\r
3587                 blackLogo = userLogo;\r
3588                 break;\r
3589             case MachinePlaysBlack:\r
3590                 whiteLogo = userLogo;\r
3591                 blackLogo = first.programLogo;\r
3592           }\r
3593         }\r
3594         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3595         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3596         ReleaseDC(hwndMain, hdc);\r
3597   }\r
3598 }\r
3599 \r
3600 void\r
3601 UpdateLogos(int display)\r
3602 { // called after loading new engine(s), in tourney or from menu\r
3603   LoadLogo(&first, 0, FALSE);\r
3604   LoadLogo(&second, 1, appData.icsActive);\r
3605   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3606   if(display) DisplayLogos();\r
3607 }\r
3608 \r
3609 static HDC hdcSeek;\r
3610 \r
3611 // [HGM] seekgraph\r
3612 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3613 {\r
3614     POINT stPt;\r
3615     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3616     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3617     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3618     SelectObject( hdcSeek, hp );\r
3619 }\r
3620 \r
3621 // front-end wrapper for drawing functions to do rectangles\r
3622 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3623 {\r
3624     HPEN hp;\r
3625     RECT rc;\r
3626 \r
3627     if (hdcSeek == NULL) {\r
3628     hdcSeek = GetDC(hwndMain);\r
3629       if (!appData.monoMode) {\r
3630         SelectPalette(hdcSeek, hPal, FALSE);\r
3631         RealizePalette(hdcSeek);\r
3632       }\r
3633     }\r
3634     hp = SelectObject( hdcSeek, gridPen );\r
3635     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3636     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3637     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3638     SelectObject( hdcSeek, hp );\r
3639 }\r
3640 \r
3641 // front-end wrapper for putting text in graph\r
3642 void DrawSeekText(char *buf, int x, int y)\r
3643 {\r
3644         SIZE stSize;\r
3645         SetBkMode( hdcSeek, TRANSPARENT );\r
3646         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3647         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3648 }\r
3649 \r
3650 void DrawSeekDot(int x, int y, int color)\r
3651 {\r
3652         int square = color & 0x80;\r
3653         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3654                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3655         color &= 0x7F;\r
3656         if(square)\r
3657             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3658                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3659         else\r
3660             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3661                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3662             SelectObject(hdcSeek, oldBrush);\r
3663 }\r
3664 \r
3665 void DrawSeekOpen()\r
3666 {\r
3667 }\r
3668 \r
3669 void DrawSeekClose()\r
3670 {\r
3671 }\r
3672 \r
3673 VOID\r
3674 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3675 {\r
3676   static Board lastReq[2], lastDrawn[2];\r
3677   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3678   static int lastDrawnFlipView = 0;\r
3679   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3680   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3681   HDC tmphdc;\r
3682   HDC hdcmem;\r
3683   HBITMAP bufferBitmap;\r
3684   HBITMAP oldBitmap;\r
3685   RECT Rect;\r
3686   HRGN clips[MAX_CLIPS];\r
3687   ChessSquare dragged_piece = EmptySquare;\r
3688   int nr = twoBoards*partnerUp;\r
3689 \r
3690   /* I'm undecided on this - this function figures out whether a full\r
3691    * repaint is necessary on its own, so there's no real reason to have the\r
3692    * caller tell it that.  I think this can safely be set to FALSE - but\r
3693    * if we trust the callers not to request full repaints unnessesarily, then\r
3694    * we could skip some clipping work.  In other words, only request a full\r
3695    * redraw when the majority of pieces have changed positions (ie. flip, \r
3696    * gamestart and similar)  --Hawk\r
3697    */\r
3698   Boolean fullrepaint = repaint;\r
3699 \r
3700   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3701 \r
3702   if( DrawPositionNeedsFullRepaint() ) {\r
3703       fullrepaint = TRUE;\r
3704   }\r
3705 \r
3706   if (board == NULL) {\r
3707     if (!lastReqValid[nr]) {\r
3708       return;\r
3709     }\r
3710     board = lastReq[nr];\r
3711   } else {\r
3712     CopyBoard(lastReq[nr], board);\r
3713     lastReqValid[nr] = 1;\r
3714   }\r
3715 \r
3716   if (doingSizing) {\r
3717     return;\r
3718   }\r
3719 \r
3720   if (IsIconic(hwndMain)) {\r
3721     return;\r
3722   }\r
3723 \r
3724   if (hdc == NULL) {\r
3725     hdc = GetDC(hwndMain);\r
3726     if (!appData.monoMode) {\r
3727       SelectPalette(hdc, hPal, FALSE);\r
3728       RealizePalette(hdc);\r
3729     }\r
3730     releaseDC = TRUE;\r
3731   } else {\r
3732     releaseDC = FALSE;\r
3733   }\r
3734 \r
3735   /* Create some work-DCs */\r
3736   hdcmem = CreateCompatibleDC(hdc);\r
3737   tmphdc = CreateCompatibleDC(hdc);\r
3738 \r
3739   /* If dragging is in progress, we temporarely remove the piece */\r
3740   /* [HGM] or temporarily decrease count if stacked              */\r
3741   /*       !! Moved to before board compare !!                   */\r
3742   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3743     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3744     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3745             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3746         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3747     } else \r
3748     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3749             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3750         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3751     } else \r
3752         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3753   }\r
3754 \r
3755   /* Figure out which squares need updating by comparing the \r
3756    * newest board with the last drawn board and checking if\r
3757    * flipping has changed.\r
3758    */\r
3759   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3760     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3761       for (column = 0; column < BOARD_WIDTH; column++) {\r
3762         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3763           SquareToPos(row, column, &x, &y);\r
3764           clips[num_clips++] =\r
3765             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3766         }\r
3767       }\r
3768     }\r
3769    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3770     for (i=0; i<2; i++) {\r
3771       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3772           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3773         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3774             lastDrawnHighlight.sq[i].y >= 0) {\r
3775           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3776                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3777           clips[num_clips++] =\r
3778             CreateRectRgn(x - lineGap, y - lineGap, \r
3779                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3780         }\r
3781         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3782           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3783           clips[num_clips++] =\r
3784             CreateRectRgn(x - lineGap, y - lineGap, \r
3785                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3786         }\r
3787       }\r
3788     }\r
3789     for (i=0; i<2; i++) {\r
3790       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3791           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3792         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3793             lastDrawnPremove.sq[i].y >= 0) {\r
3794           SquareToPos(lastDrawnPremove.sq[i].y,\r
3795                       lastDrawnPremove.sq[i].x, &x, &y);\r
3796           clips[num_clips++] =\r
3797             CreateRectRgn(x - lineGap, y - lineGap, \r
3798                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3799         }\r
3800         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3801             premoveHighlightInfo.sq[i].y >= 0) {\r
3802           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3803                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3804           clips[num_clips++] =\r
3805             CreateRectRgn(x - lineGap, y - lineGap, \r
3806                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3807         }\r
3808       }\r
3809     }\r
3810    } else { // nr == 1\r
3811         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3812         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3813         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3814         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3815       for (i=0; i<2; i++) {\r
3816         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3817             partnerHighlightInfo.sq[i].y >= 0) {\r
3818           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3819                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3820           clips[num_clips++] =\r
3821             CreateRectRgn(x - lineGap, y - lineGap, \r
3822                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3823         }\r
3824         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3825             oldPartnerHighlight.sq[i].y >= 0) {\r
3826           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3827                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3828           clips[num_clips++] =\r
3829             CreateRectRgn(x - lineGap, y - lineGap, \r
3830                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3831         }\r
3832       }\r
3833    }\r
3834   } else {\r
3835     fullrepaint = TRUE;\r
3836   }\r
3837 \r
3838   /* Create a buffer bitmap - this is the actual bitmap\r
3839    * being written to.  When all the work is done, we can\r
3840    * copy it to the real DC (the screen).  This avoids\r
3841    * the problems with flickering.\r
3842    */\r
3843   GetClientRect(hwndMain, &Rect);\r
3844   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3845                                         Rect.bottom-Rect.top+1);\r
3846   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3847   if (!appData.monoMode) {\r
3848     SelectPalette(hdcmem, hPal, FALSE);\r
3849   }\r
3850 \r
3851   /* Create clips for dragging */\r
3852   if (!fullrepaint) {\r
3853     if (dragInfo.from.x >= 0) {\r
3854       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3855       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3856     }\r
3857     if (dragInfo.start.x >= 0) {\r
3858       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3859       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3860     }\r
3861     if (dragInfo.pos.x >= 0) {\r
3862       x = dragInfo.pos.x - squareSize / 2;\r
3863       y = dragInfo.pos.y - squareSize / 2;\r
3864       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3865     }\r
3866     if (dragInfo.lastpos.x >= 0) {\r
3867       x = dragInfo.lastpos.x - squareSize / 2;\r
3868       y = dragInfo.lastpos.y - squareSize / 2;\r
3869       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3870     }\r
3871   }\r
3872 \r
3873   /* Are we animating a move?  \r
3874    * If so, \r
3875    *   - remove the piece from the board (temporarely)\r
3876    *   - calculate the clipping region\r
3877    */\r
3878   if (!fullrepaint) {\r
3879     if (animInfo.piece != EmptySquare) {\r
3880       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3881       x = boardRect.left + animInfo.lastpos.x;\r
3882       y = boardRect.top + animInfo.lastpos.y;\r
3883       x2 = boardRect.left + animInfo.pos.x;\r
3884       y2 = boardRect.top + animInfo.pos.y;\r
3885       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3886       /* Slight kludge.  The real problem is that after AnimateMove is\r
3887          done, the position on the screen does not match lastDrawn.\r
3888          This currently causes trouble only on e.p. captures in\r
3889          atomic, where the piece moves to an empty square and then\r
3890          explodes.  The old and new positions both had an empty square\r
3891          at the destination, but animation has drawn a piece there and\r
3892          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3893       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3894     }\r
3895   }\r
3896 \r
3897   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3898   if (num_clips == 0)\r
3899     fullrepaint = TRUE;\r
3900 \r
3901   /* Set clipping on the memory DC */\r
3902   if (!fullrepaint) {\r
3903     SelectClipRgn(hdcmem, clips[0]);\r
3904     for (x = 1; x < num_clips; x++) {\r
3905       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3906         abort();  // this should never ever happen!\r
3907     }\r
3908   }\r
3909 \r
3910   /* Do all the drawing to the memory DC */\r
3911   if(explodeInfo.radius) { // [HGM] atomic\r
3912         HBRUSH oldBrush;\r
3913         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3914         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3915         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3916         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3917         x += squareSize/2;\r
3918         y += squareSize/2;\r
3919         if(!fullrepaint) {\r
3920           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3921           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3922         }\r
3923         DrawGridOnDC(hdcmem);\r
3924         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3925         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3926         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3927         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3928         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3929         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3930         SelectObject(hdcmem, oldBrush);\r
3931   } else {\r
3932     if(border) DrawBackgroundOnDC(hdcmem);\r
3933     DrawGridOnDC(hdcmem);\r
3934     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3935         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3936         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3937     } else {\r
3938         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3939         oldPartnerHighlight = partnerHighlightInfo;\r
3940     }\r
3941     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3942   }\r
3943   if(nr == 0) // [HGM] dual: markers only on left board\r
3944   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3945     for (column = 0; column < BOARD_WIDTH; column++) {\r
3946         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3947             HBRUSH oldBrush = SelectObject(hdcmem, \r
3948                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3949             SquareToPos(row, column, &x, &y);\r
3950             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3951                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3952             SelectObject(hdcmem, oldBrush);\r
3953         }\r
3954     }\r
3955   }\r
3956 \r
3957   if( appData.highlightMoveWithArrow ) {\r
3958     DrawArrowHighlight(hdcmem);\r
3959   }\r
3960 \r
3961   DrawCoordsOnDC(hdcmem);\r
3962 \r
3963   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3964                  /* to make sure lastDrawn contains what is actually drawn */\r
3965 \r
3966   /* Put the dragged piece back into place and draw it (out of place!) */\r
3967     if (dragged_piece != EmptySquare) {\r
3968     /* [HGM] or restack */\r
3969     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3970                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3971     else\r
3972     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3973                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3974     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3975     x = dragInfo.pos.x - squareSize / 2;\r
3976     y = dragInfo.pos.y - squareSize / 2;\r
3977     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3978                   ((int) dragInfo.piece < (int) BlackPawn), \r
3979                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3980   }   \r
3981   \r
3982   /* Put the animated piece back into place and draw it */\r
3983   if (animInfo.piece != EmptySquare) {\r
3984     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3985     x = boardRect.left + animInfo.pos.x;\r
3986     y = boardRect.top + animInfo.pos.y;\r
3987     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3988                   ((int) animInfo.piece < (int) BlackPawn),\r
3989                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3990   }\r
3991 \r
3992   /* Release the bufferBitmap by selecting in the old bitmap \r
3993    * and delete the memory DC\r
3994    */\r
3995   SelectObject(hdcmem, oldBitmap);\r
3996   DeleteDC(hdcmem);\r
3997 \r
3998   /* Set clipping on the target DC */\r
3999   if (!fullrepaint) {\r
4000     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4001         RECT rect;\r
4002         GetRgnBox(clips[x], &rect);\r
4003         DeleteObject(clips[x]);\r
4004         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4005                           rect.right + wpMain.width/2, rect.bottom);\r
4006     }\r
4007     SelectClipRgn(hdc, clips[0]);\r
4008     for (x = 1; x < num_clips; x++) {\r
4009       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4010         abort();   // this should never ever happen!\r
4011     } \r
4012   }\r
4013 \r
4014   /* Copy the new bitmap onto the screen in one go.\r
4015    * This way we avoid any flickering\r
4016    */\r
4017   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4018   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4019          boardRect.right - boardRect.left,\r
4020          boardRect.bottom - boardRect.top,\r
4021          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4022   if(saveDiagFlag) { \r
4023     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4024     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4025 \r
4026     GetObject(bufferBitmap, sizeof(b), &b);\r
4027     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4028         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4029         bih.biWidth = b.bmWidth;\r
4030         bih.biHeight = b.bmHeight;\r
4031         bih.biPlanes = 1;\r
4032         bih.biBitCount = b.bmBitsPixel;\r
4033         bih.biCompression = 0;\r
4034         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4035         bih.biXPelsPerMeter = 0;\r
4036         bih.biYPelsPerMeter = 0;\r
4037         bih.biClrUsed = 0;\r
4038         bih.biClrImportant = 0;\r
4039 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4040 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4041         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4042 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4043 \r
4044         wb = b.bmWidthBytes;\r
4045         // count colors\r
4046         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4047                 int k = ((int*) pData)[i];\r
4048                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4049                 if(j >= 16) break;\r
4050                 color[j] = k;\r
4051                 if(j >= nrColors) nrColors = j+1;\r
4052         }\r
4053         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4054                 INT p = 0;\r
4055                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4056                     for(w=0; w<(wb>>2); w+=2) {\r
4057                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4058                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4059                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4060                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4061                         pData[p++] = m | j<<4;\r
4062                     }\r
4063                     while(p&3) pData[p++] = 0;\r
4064                 }\r
4065                 fac = 3;\r
4066                 wb = ((wb+31)>>5)<<2;\r
4067         }\r
4068         // write BITMAPFILEHEADER\r
4069         fprintf(diagFile, "BM");\r
4070         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4071         fputDW(diagFile, 0);\r
4072         fputDW(diagFile, 0x36 + (fac?64:0));\r
4073         // write BITMAPINFOHEADER\r
4074         fputDW(diagFile, 40);\r
4075         fputDW(diagFile, b.bmWidth);\r
4076         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4077         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4078         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4079         fputDW(diagFile, 0);\r
4080         fputDW(diagFile, 0);\r
4081         fputDW(diagFile, 0);\r
4082         fputDW(diagFile, 0);\r
4083         fputDW(diagFile, 0);\r
4084         fputDW(diagFile, 0);\r
4085         // write color table\r
4086         if(fac)\r
4087         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4088         // write bitmap data\r
4089         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4090                 fputc(pData[i], diagFile);\r
4091         free(pData);\r
4092      }\r
4093   }\r
4094 \r
4095   SelectObject(tmphdc, oldBitmap);\r
4096 \r
4097   /* Massive cleanup */\r
4098   for (x = 0; x < num_clips; x++)\r
4099     DeleteObject(clips[x]);\r
4100 \r
4101   DeleteDC(tmphdc);\r
4102   DeleteObject(bufferBitmap);\r
4103 \r
4104   if (releaseDC) \r
4105     ReleaseDC(hwndMain, hdc);\r
4106   \r
4107   if (lastDrawnFlipView != flipView && nr == 0) {\r
4108     if (flipView)\r
4109       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4110     else\r
4111       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4112   }\r
4113 \r
4114 /*  CopyBoard(lastDrawn, board);*/\r
4115   lastDrawnHighlight = highlightInfo;\r
4116   lastDrawnPremove   = premoveHighlightInfo;\r
4117   lastDrawnFlipView = flipView;\r
4118   lastDrawnValid[nr] = 1;\r
4119 }\r
4120 \r
4121 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4122 int\r
4123 SaveDiagram(f)\r
4124      FILE *f;\r
4125 {\r
4126     saveDiagFlag = 1; diagFile = f;\r
4127     HDCDrawPosition(NULL, TRUE, NULL);\r
4128     saveDiagFlag = 0;\r
4129 \r
4130     fclose(f);\r
4131     return TRUE;\r
4132 }\r
4133 \r
4134 \r
4135 /*---------------------------------------------------------------------------*\\r
4136 | CLIENT PAINT PROCEDURE\r
4137 |   This is the main event-handler for the WM_PAINT message.\r
4138 |\r
4139 \*---------------------------------------------------------------------------*/\r
4140 VOID\r
4141 PaintProc(HWND hwnd)\r
4142 {\r
4143   HDC         hdc;\r
4144   PAINTSTRUCT ps;\r
4145   HFONT       oldFont;\r
4146 \r
4147   if((hdc = BeginPaint(hwnd, &ps))) {\r
4148     if (IsIconic(hwnd)) {\r
4149       DrawIcon(hdc, 2, 2, iconCurrent);\r
4150     } else {\r
4151       if (!appData.monoMode) {\r
4152         SelectPalette(hdc, hPal, FALSE);\r
4153         RealizePalette(hdc);\r
4154       }\r
4155       HDCDrawPosition(hdc, 1, NULL);\r
4156       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4157         flipView = !flipView; partnerUp = !partnerUp;\r
4158         HDCDrawPosition(hdc, 1, NULL);\r
4159         flipView = !flipView; partnerUp = !partnerUp;\r
4160       }\r
4161       oldFont =\r
4162         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4163       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4164                  ETO_CLIPPED|ETO_OPAQUE,\r
4165                  &messageRect, messageText, strlen(messageText), NULL);\r
4166       SelectObject(hdc, oldFont);\r
4167       DisplayBothClocks();\r
4168       DisplayLogos();\r
4169     }\r
4170     EndPaint(hwnd,&ps);\r
4171   }\r
4172 \r
4173   return;\r
4174 }\r
4175 \r
4176 \r
4177 /*\r
4178  * If the user selects on a border boundary, return -1; if off the board,\r
4179  *   return -2.  Otherwise map the event coordinate to the square.\r
4180  * The offset boardRect.left or boardRect.top must already have been\r
4181  *   subtracted from x.\r
4182  */\r
4183 int EventToSquare(x, limit)\r
4184      int x, limit;\r
4185 {\r
4186   if (x <= border)\r
4187     return -2;\r
4188   if (x < lineGap + border)\r
4189     return -1;\r
4190   x -= lineGap + border;\r
4191   if ((x % (squareSize + lineGap)) >= squareSize)\r
4192     return -1;\r
4193   x /= (squareSize + lineGap);\r
4194     if (x >= limit)\r
4195     return -2;\r
4196   return x;\r
4197 }\r
4198 \r
4199 typedef struct {\r
4200   char piece;\r
4201   int command;\r
4202   char* name;\r
4203 } DropEnable;\r
4204 \r
4205 DropEnable dropEnables[] = {\r
4206   { 'P', DP_Pawn, N_("Pawn") },\r
4207   { 'N', DP_Knight, N_("Knight") },\r
4208   { 'B', DP_Bishop, N_("Bishop") },\r
4209   { 'R', DP_Rook, N_("Rook") },\r
4210   { 'Q', DP_Queen, N_("Queen") },\r
4211 };\r
4212 \r
4213 VOID\r
4214 SetupDropMenu(HMENU hmenu)\r
4215 {\r
4216   int i, count, enable;\r
4217   char *p;\r
4218   extern char white_holding[], black_holding[];\r
4219   char item[MSG_SIZ];\r
4220 \r
4221   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4222     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4223                dropEnables[i].piece);\r
4224     count = 0;\r
4225     while (p && *p++ == dropEnables[i].piece) count++;\r
4226       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4227     enable = count > 0 || !appData.testLegality\r
4228       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4229                       && !appData.icsActive);\r
4230     ModifyMenu(hmenu, dropEnables[i].command,\r
4231                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4232                dropEnables[i].command, item);\r
4233   }\r
4234 }\r
4235 \r
4236 void DragPieceBegin(int x, int y, Boolean instantly)\r
4237 {\r
4238       dragInfo.lastpos.x = boardRect.left + x;\r
4239       dragInfo.lastpos.y = boardRect.top + y;\r
4240       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4241       dragInfo.from.x = fromX;\r
4242       dragInfo.from.y = fromY;\r
4243       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4244       dragInfo.start = dragInfo.from;\r
4245       SetCapture(hwndMain);\r
4246 }\r
4247 \r
4248 void DragPieceEnd(int x, int y)\r
4249 {\r
4250     ReleaseCapture();\r
4251     dragInfo.start.x = dragInfo.start.y = -1;\r
4252     dragInfo.from = dragInfo.start;\r
4253     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4254 }\r
4255 \r
4256 void ChangeDragPiece(ChessSquare piece)\r
4257 {\r
4258     dragInfo.piece = piece;\r
4259 }\r
4260 \r
4261 /* Event handler for mouse messages */\r
4262 VOID\r
4263 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4264 {\r
4265   int x, y, menuNr;\r
4266   POINT pt;\r
4267   static int recursive = 0;\r
4268   HMENU hmenu;\r
4269   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4270 \r
4271   if (recursive) {\r
4272     if (message == WM_MBUTTONUP) {\r
4273       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4274          to the middle button: we simulate pressing the left button too!\r
4275          */\r
4276       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4277       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4278     }\r
4279     return;\r
4280   }\r
4281   recursive++;\r
4282   \r
4283   pt.x = LOWORD(lParam);\r
4284   pt.y = HIWORD(lParam);\r
4285   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4286   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4287   if (!flipView && y >= 0) {\r
4288     y = BOARD_HEIGHT - 1 - y;\r
4289   }\r
4290   if (flipView && x >= 0) {\r
4291     x = BOARD_WIDTH - 1 - x;\r
4292   }\r
4293 \r
4294   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4295   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4296 \r
4297   switch (message) {\r
4298   case WM_LBUTTONDOWN:\r
4299       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4300         ClockClick(flipClock); break;\r
4301       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4302         ClockClick(!flipClock); break;\r
4303       }\r
4304       dragInfo.start.x = dragInfo.start.y = -1;\r
4305       dragInfo.from = dragInfo.start;\r
4306     if(fromX == -1 && frozen) { // not sure where this is for\r
4307                 fromX = fromY = -1; \r
4308       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4309       break;\r
4310     }\r
4311       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4312       DrawPosition(TRUE, NULL);\r
4313     break;\r
4314 \r
4315   case WM_LBUTTONUP:\r
4316       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4317       DrawPosition(TRUE, NULL);\r
4318     break;\r
4319 \r
4320   case WM_MOUSEMOVE:\r
4321     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4322     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4323     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4324     if ((appData.animateDragging || appData.highlightDragging)\r
4325         && (wParam & MK_LBUTTON)\r
4326         && dragInfo.from.x >= 0) \r
4327     {\r
4328       BOOL full_repaint = FALSE;\r
4329 \r
4330       if (appData.animateDragging) {\r
4331         dragInfo.pos = pt;\r
4332       }\r
4333       if (appData.highlightDragging) {\r
4334         SetHighlights(fromX, fromY, x, y);\r
4335         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4336             full_repaint = TRUE;\r
4337         }\r
4338       }\r
4339       \r
4340       DrawPosition( full_repaint, NULL);\r
4341       \r
4342       dragInfo.lastpos = dragInfo.pos;\r
4343     }\r
4344     break;\r
4345 \r
4346   case WM_MOUSEWHEEL: // [DM]\r
4347     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4348        /* Mouse Wheel is being rolled forward\r
4349         * Play moves forward\r
4350         */\r
4351        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4352                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4353        /* Mouse Wheel is being rolled backward\r
4354         * Play moves backward\r
4355         */\r
4356        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4357                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4358     }\r
4359     break;\r
4360 \r
4361   case WM_MBUTTONUP:\r
4362   case WM_RBUTTONUP:\r
4363     ReleaseCapture();\r
4364     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4365     break;\r
4366  \r
4367   case WM_MBUTTONDOWN:\r
4368   case WM_RBUTTONDOWN:\r
4369     ErrorPopDown();\r
4370     ReleaseCapture();\r
4371     fromX = fromY = -1;\r
4372     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4373     dragInfo.start.x = dragInfo.start.y = -1;\r
4374     dragInfo.from = dragInfo.start;\r
4375     dragInfo.lastpos = dragInfo.pos;\r
4376     if (appData.highlightDragging) {\r
4377       ClearHighlights();\r
4378     }\r
4379     if(y == -2) {\r
4380       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4381       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4382           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4383       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4384           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4385       }\r
4386       break;\r
4387     }\r
4388     DrawPosition(TRUE, NULL);\r
4389 \r
4390     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4391     switch (menuNr) {\r
4392     case 0:\r
4393       if (message == WM_MBUTTONDOWN) {\r
4394         buttonCount = 3;  /* even if system didn't think so */\r
4395         if (wParam & MK_SHIFT) \r
4396           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4397         else\r
4398           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4399       } else { /* message == WM_RBUTTONDOWN */\r
4400         /* Just have one menu, on the right button.  Windows users don't\r
4401            think to try the middle one, and sometimes other software steals\r
4402            it, or it doesn't really exist. */\r
4403         if(gameInfo.variant != VariantShogi)\r
4404             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4405         else\r
4406             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4407       }\r
4408       break;\r
4409     case 2:\r
4410       SetCapture(hwndMain);\r
4411       break;\r
4412     case 1:\r
4413       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4414       SetupDropMenu(hmenu);\r
4415       MenuPopup(hwnd, pt, hmenu, -1);\r
4416     default:\r
4417       break;\r
4418     }\r
4419     break;\r
4420   }\r
4421 \r
4422   recursive--;\r
4423 }\r
4424 \r
4425 /* Preprocess messages for buttons in main window */\r
4426 LRESULT CALLBACK\r
4427 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4428 {\r
4429   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4430   int i, dir;\r
4431 \r
4432   for (i=0; i<N_BUTTONS; i++) {\r
4433     if (buttonDesc[i].id == id) break;\r
4434   }\r
4435   if (i == N_BUTTONS) return 0;\r
4436   switch (message) {\r
4437   case WM_KEYDOWN:\r
4438     switch (wParam) {\r
4439     case VK_LEFT:\r
4440     case VK_RIGHT:\r
4441       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4442       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4443       return TRUE;\r
4444     }\r
4445     break;\r
4446   case WM_CHAR:\r
4447     switch (wParam) {\r
4448     case '\r':\r
4449       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4450       return TRUE;\r
4451     default:\r
4452       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4453         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4454         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4455         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4456         SetFocus(h);\r
4457         SendMessage(h, WM_CHAR, wParam, lParam);\r
4458         return TRUE;\r
4459       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4460         TypeInEvent((char)wParam);\r
4461       }\r
4462       break;\r
4463     }\r
4464     break;\r
4465   }\r
4466   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4467 }\r
4468 \r
4469 /* Process messages for Promotion dialog box */\r
4470 LRESULT CALLBACK\r
4471 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4472 {\r
4473   char promoChar;\r
4474 \r
4475   switch (message) {\r
4476   case WM_INITDIALOG: /* message: initialize dialog box */\r
4477     /* Center the dialog over the application window */\r
4478     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4479     Translate(hDlg, DLG_PromotionKing);\r
4480     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4481       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4482        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4483        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4484                SW_SHOW : SW_HIDE);\r
4485     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4486     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4487        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4488          PieceToChar(WhiteAngel) != '~') ||\r
4489         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4490          PieceToChar(BlackAngel) != '~')   ) ?\r
4491                SW_SHOW : SW_HIDE);\r
4492     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4493        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4494          PieceToChar(WhiteMarshall) != '~') ||\r
4495         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4496          PieceToChar(BlackMarshall) != '~')   ) ?\r
4497                SW_SHOW : SW_HIDE);\r
4498     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4499     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4500        gameInfo.variant != VariantShogi ?\r
4501                SW_SHOW : SW_HIDE);\r
4502     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4503        gameInfo.variant != VariantShogi ?\r
4504                SW_SHOW : SW_HIDE);\r
4505     if(gameInfo.variant == VariantShogi) {\r
4506         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4507         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4508         SetWindowText(hDlg, "Promote?");\r
4509     }\r
4510     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4511        gameInfo.variant == VariantSuper ?\r
4512                SW_SHOW : SW_HIDE);\r
4513     return TRUE;\r
4514 \r
4515   case WM_COMMAND: /* message: received a command */\r
4516     switch (LOWORD(wParam)) {\r
4517     case IDCANCEL:\r
4518       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4519       ClearHighlights();\r
4520       DrawPosition(FALSE, NULL);\r
4521       return TRUE;\r
4522     case PB_King:\r
4523       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4524       break;\r
4525     case PB_Queen:\r
4526       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4527       break;\r
4528     case PB_Rook:\r
4529       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4530       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4531       break;\r
4532     case PB_Bishop:\r
4533       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4534       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4535       break;\r
4536     case PB_Chancellor:\r
4537       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4538       break;\r
4539     case PB_Archbishop:\r
4540       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4541       break;\r
4542     case PB_Knight:\r
4543       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4544       break;\r
4545     default:\r
4546       return FALSE;\r
4547     }\r
4548     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4549     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4550     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4551     fromX = fromY = -1;\r
4552     if (!appData.highlightLastMove) {\r
4553       ClearHighlights();\r
4554       DrawPosition(FALSE, NULL);\r
4555     }\r
4556     return TRUE;\r
4557   }\r
4558   return FALSE;\r
4559 }\r
4560 \r
4561 /* Pop up promotion dialog */\r
4562 VOID\r
4563 PromotionPopup(HWND hwnd)\r
4564 {\r
4565   FARPROC lpProc;\r
4566 \r
4567   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4568   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4569     hwnd, (DLGPROC)lpProc);\r
4570   FreeProcInstance(lpProc);\r
4571 }\r
4572 \r
4573 void\r
4574 PromotionPopUp()\r
4575 {\r
4576   DrawPosition(TRUE, NULL);\r
4577   PromotionPopup(hwndMain);\r
4578 }\r
4579 \r
4580 VOID\r
4581 LoadGameDialog(HWND hwnd, char* title)\r
4582 {\r
4583   UINT number = 0;\r
4584   FILE *f;\r
4585   char fileTitle[MSG_SIZ];\r
4586   f = OpenFileDialog(hwnd, "rb", "",\r
4587                      appData.oldSaveStyle ? "gam" : "pgn",\r
4588                      GAME_FILT,\r
4589                      title, &number, fileTitle, NULL);\r
4590   if (f != NULL) {\r
4591     cmailMsgLoaded = FALSE;\r
4592     if (number == 0) {\r
4593       int error = GameListBuild(f);\r
4594       if (error) {\r
4595         DisplayError(_("Cannot build game list"), error);\r
4596       } else if (!ListEmpty(&gameList) &&\r
4597                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4598         GameListPopUp(f, fileTitle);\r
4599         return;\r
4600       }\r
4601       GameListDestroy();\r
4602       number = 1;\r
4603     }\r
4604     LoadGame(f, number, fileTitle, FALSE);\r
4605   }\r
4606 }\r
4607 \r
4608 int get_term_width()\r
4609 {\r
4610     HDC hdc;\r
4611     TEXTMETRIC tm;\r
4612     RECT rc;\r
4613     HFONT hfont, hold_font;\r
4614     LOGFONT lf;\r
4615     HWND hText;\r
4616 \r
4617     if (hwndConsole)\r
4618         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4619     else\r
4620         return 79;\r
4621 \r
4622     // get the text metrics\r
4623     hdc = GetDC(hText);\r
4624     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4625     if (consoleCF.dwEffects & CFE_BOLD)\r
4626         lf.lfWeight = FW_BOLD;\r
4627     if (consoleCF.dwEffects & CFE_ITALIC)\r
4628         lf.lfItalic = TRUE;\r
4629     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4630         lf.lfStrikeOut = TRUE;\r
4631     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4632         lf.lfUnderline = TRUE;\r
4633     hfont = CreateFontIndirect(&lf);\r
4634     hold_font = SelectObject(hdc, hfont);\r
4635     GetTextMetrics(hdc, &tm);\r
4636     SelectObject(hdc, hold_font);\r
4637     DeleteObject(hfont);\r
4638     ReleaseDC(hText, hdc);\r
4639 \r
4640     // get the rectangle\r
4641     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4642 \r
4643     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4644 }\r
4645 \r
4646 void UpdateICSWidth(HWND hText)\r
4647 {\r
4648     LONG old_width, new_width;\r
4649 \r
4650     new_width = get_term_width(hText, FALSE);\r
4651     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4652     if (new_width != old_width)\r
4653     {\r
4654         ics_update_width(new_width);\r
4655         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4656     }\r
4657 }\r
4658 \r
4659 VOID\r
4660 ChangedConsoleFont()\r
4661 {\r
4662   CHARFORMAT cfmt;\r
4663   CHARRANGE tmpsel, sel;\r
4664   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4665   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4666   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4667   PARAFORMAT paraf;\r
4668 \r
4669   cfmt.cbSize = sizeof(CHARFORMAT);\r
4670   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4671     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4672                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4673   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4674    * size.  This was undocumented in the version of MSVC++ that I had\r
4675    * when I wrote the code, but is apparently documented now.\r
4676    */\r
4677   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4678   cfmt.bCharSet = f->lf.lfCharSet;\r
4679   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4680   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4681   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4682   /* Why are the following seemingly needed too? */\r
4683   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4684   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4685   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4686   tmpsel.cpMin = 0;\r
4687   tmpsel.cpMax = -1; /*999999?*/\r
4688   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4689   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4690   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4691    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4692    */\r
4693   paraf.cbSize = sizeof(paraf);\r
4694   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4695   paraf.dxStartIndent = 0;\r
4696   paraf.dxOffset = WRAP_INDENT;\r
4697   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4698   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4699   UpdateICSWidth(hText);\r
4700 }\r
4701 \r
4702 /*---------------------------------------------------------------------------*\\r
4703  *\r
4704  * Window Proc for main window\r
4705  *\r
4706 \*---------------------------------------------------------------------------*/\r
4707 \r
4708 /* Process messages for main window, etc. */\r
4709 LRESULT CALLBACK\r
4710 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4711 {\r
4712   FARPROC lpProc;\r
4713   int wmId, wmEvent;\r
4714   char *defName;\r
4715   FILE *f;\r
4716   UINT number;\r
4717   char fileTitle[MSG_SIZ];\r
4718   static SnapData sd;\r
4719   static int peek=0;\r
4720 \r
4721   switch (message) {\r
4722 \r
4723   case WM_PAINT: /* message: repaint portion of window */\r
4724     PaintProc(hwnd);\r
4725     break;\r
4726 \r
4727   case WM_ERASEBKGND:\r
4728     if (IsIconic(hwnd)) {\r
4729       /* Cheat; change the message */\r
4730       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4731     } else {\r
4732       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4733     }\r
4734     break;\r
4735 \r
4736   case WM_LBUTTONDOWN:\r
4737   case WM_MBUTTONDOWN:\r
4738   case WM_RBUTTONDOWN:\r
4739   case WM_LBUTTONUP:\r
4740   case WM_MBUTTONUP:\r
4741   case WM_RBUTTONUP:\r
4742   case WM_MOUSEMOVE:\r
4743   case WM_MOUSEWHEEL:\r
4744     MouseEvent(hwnd, message, wParam, lParam);\r
4745     break;\r
4746 \r
4747   case WM_KEYUP:\r
4748     if((char)wParam == '\b') {\r
4749       ForwardEvent(); peek = 0;\r
4750     }\r
4751 \r
4752     JAWS_KBUP_NAVIGATION\r
4753 \r
4754     break;\r
4755 \r
4756   case WM_KEYDOWN:\r
4757     if((char)wParam == '\b') {\r
4758       if(!peek) BackwardEvent(), peek = 1;\r
4759     }\r
4760 \r
4761     JAWS_KBDOWN_NAVIGATION\r
4762 \r
4763     break;\r
4764 \r
4765   case WM_CHAR:\r
4766     \r
4767     JAWS_ALT_INTERCEPT\r
4768 \r
4769     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4770         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4771         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4772         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4773         SetFocus(h);\r
4774         SendMessage(h, message, wParam, lParam);\r
4775     } else if(lParam != KF_REPEAT) {\r
4776         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4777                 TypeInEvent((char)wParam);\r
4778         } else if((char)wParam == 003) CopyGameToClipboard();\r
4779          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4780     }\r
4781 \r
4782     break;\r
4783 \r
4784   case WM_PALETTECHANGED:\r
4785     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4786       int nnew;\r
4787       HDC hdc = GetDC(hwndMain);\r
4788       SelectPalette(hdc, hPal, TRUE);\r
4789       nnew = RealizePalette(hdc);\r
4790       if (nnew > 0) {\r
4791         paletteChanged = TRUE;\r
4792         InvalidateRect(hwnd, &boardRect, FALSE);\r
4793       }\r
4794       ReleaseDC(hwnd, hdc);\r
4795     }\r
4796     break;\r
4797 \r
4798   case WM_QUERYNEWPALETTE:\r
4799     if (!appData.monoMode /*&& paletteChanged*/) {\r
4800       int nnew;\r
4801       HDC hdc = GetDC(hwndMain);\r
4802       paletteChanged = FALSE;\r
4803       SelectPalette(hdc, hPal, FALSE);\r
4804       nnew = RealizePalette(hdc);\r
4805       if (nnew > 0) {\r
4806         InvalidateRect(hwnd, &boardRect, FALSE);\r
4807       }\r
4808       ReleaseDC(hwnd, hdc);\r
4809       return TRUE;\r
4810     }\r
4811     return FALSE;\r
4812 \r
4813   case WM_COMMAND: /* message: command from application menu */\r
4814     wmId    = LOWORD(wParam);\r
4815     wmEvent = HIWORD(wParam);\r
4816 \r
4817     switch (wmId) {\r
4818     case IDM_NewGame:\r
4819       ResetGameEvent();\r
4820       SAY("new game enter a move to play against the computer with white");\r
4821       break;\r
4822 \r
4823     case IDM_NewGameFRC:\r
4824       if( NewGameFRC() == 0 ) {\r
4825         ResetGameEvent();\r
4826       }\r
4827       break;\r
4828 \r
4829     case IDM_NewVariant:\r
4830       NewVariantPopup(hwnd);\r
4831       break;\r
4832 \r
4833     case IDM_LoadGame:\r
4834       LoadGameDialog(hwnd, _("Load Game from File"));\r
4835       break;\r
4836 \r
4837     case IDM_LoadNextGame:\r
4838       ReloadGame(1);\r
4839       break;\r
4840 \r
4841     case IDM_LoadPrevGame:\r
4842       ReloadGame(-1);\r
4843       break;\r
4844 \r
4845     case IDM_ReloadGame:\r
4846       ReloadGame(0);\r
4847       break;\r
4848 \r
4849     case IDM_LoadPosition:\r
4850       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4851         Reset(FALSE, TRUE);\r
4852       }\r
4853       number = 1;\r
4854       f = OpenFileDialog(hwnd, "rb", "",\r
4855                          appData.oldSaveStyle ? "pos" : "fen",\r
4856                          POSITION_FILT,\r
4857                          _("Load Position from File"), &number, fileTitle, NULL);\r
4858       if (f != NULL) {\r
4859         LoadPosition(f, number, fileTitle);\r
4860       }\r
4861       break;\r
4862 \r
4863     case IDM_LoadNextPosition:\r
4864       ReloadPosition(1);\r
4865       break;\r
4866 \r
4867     case IDM_LoadPrevPosition:\r
4868       ReloadPosition(-1);\r
4869       break;\r
4870 \r
4871     case IDM_ReloadPosition:\r
4872       ReloadPosition(0);\r
4873       break;\r
4874 \r
4875     case IDM_SaveGame:\r
4876       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4877       f = OpenFileDialog(hwnd, "a", defName,\r
4878                          appData.oldSaveStyle ? "gam" : "pgn",\r
4879                          GAME_FILT,\r
4880                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4881       if (f != NULL) {\r
4882         SaveGame(f, 0, "");\r
4883       }\r
4884       break;\r
4885 \r
4886     case IDM_SavePosition:\r
4887       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4888       f = OpenFileDialog(hwnd, "a", defName,\r
4889                          appData.oldSaveStyle ? "pos" : "fen",\r
4890                          POSITION_FILT,\r
4891                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4892       if (f != NULL) {\r
4893         SavePosition(f, 0, "");\r
4894       }\r
4895       break;\r
4896 \r
4897     case IDM_SaveDiagram:\r
4898       defName = "diagram";\r
4899       f = OpenFileDialog(hwnd, "wb", defName,\r
4900                          "bmp",\r
4901                          DIAGRAM_FILT,\r
4902                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4903       if (f != NULL) {\r
4904         SaveDiagram(f);\r
4905       }\r
4906       break;\r
4907 \r
4908     case IDM_CreateBook:\r
4909       CreateBookEvent();\r
4910       break;\r
4911 \r
4912     case IDM_CopyGame:\r
4913       CopyGameToClipboard();\r
4914       break;\r
4915 \r
4916     case IDM_PasteGame:\r
4917       PasteGameFromClipboard();\r
4918       break;\r
4919 \r
4920     case IDM_CopyGameListToClipboard:\r
4921       CopyGameListToClipboard();\r
4922       break;\r
4923 \r
4924     /* [AS] Autodetect FEN or PGN data */\r
4925     case IDM_PasteAny:\r
4926       PasteGameOrFENFromClipboard();\r
4927       break;\r
4928 \r
4929     /* [AS] Move history */\r
4930     case IDM_ShowMoveHistory:\r
4931         if( MoveHistoryIsUp() ) {\r
4932             MoveHistoryPopDown();\r
4933         }\r
4934         else {\r
4935             MoveHistoryPopUp();\r
4936         }\r
4937         break;\r
4938 \r
4939     /* [AS] Eval graph */\r
4940     case IDM_ShowEvalGraph:\r
4941         if( EvalGraphIsUp() ) {\r
4942             EvalGraphPopDown();\r
4943         }\r
4944         else {\r
4945             EvalGraphPopUp();\r
4946             SetFocus(hwndMain);\r
4947         }\r
4948         break;\r
4949 \r
4950     /* [AS] Engine output */\r
4951     case IDM_ShowEngineOutput:\r
4952         if( EngineOutputIsUp() ) {\r
4953             EngineOutputPopDown();\r
4954         }\r
4955         else {\r
4956             EngineOutputPopUp();\r
4957         }\r
4958         break;\r
4959 \r
4960     /* [AS] User adjudication */\r
4961     case IDM_UserAdjudication_White:\r
4962         UserAdjudicationEvent( +1 );\r
4963         break;\r
4964 \r
4965     case IDM_UserAdjudication_Black:\r
4966         UserAdjudicationEvent( -1 );\r
4967         break;\r
4968 \r
4969     case IDM_UserAdjudication_Draw:\r
4970         UserAdjudicationEvent( 0 );\r
4971         break;\r
4972 \r
4973     /* [AS] Game list options dialog */\r
4974     case IDM_GameListOptions:\r
4975       GameListOptions();\r
4976       break;\r
4977 \r
4978     case IDM_NewChat:\r
4979       ChatPopUp(NULL);\r
4980       break;\r
4981 \r
4982     case IDM_CopyPosition:\r
4983       CopyFENToClipboard();\r
4984       break;\r
4985 \r
4986     case IDM_PastePosition:\r
4987       PasteFENFromClipboard();\r
4988       break;\r
4989 \r
4990     case IDM_MailMove:\r
4991       MailMoveEvent();\r
4992       break;\r
4993 \r
4994     case IDM_ReloadCMailMsg:\r
4995       Reset(TRUE, TRUE);\r
4996       ReloadCmailMsgEvent(FALSE);\r
4997       break;\r
4998 \r
4999     case IDM_Minimize:\r
5000       ShowWindow(hwnd, SW_MINIMIZE);\r
5001       break;\r
5002 \r
5003     case IDM_Exit:\r
5004       ExitEvent(0);\r
5005       break;\r
5006 \r
5007     case IDM_MachineWhite:\r
5008       MachineWhiteEvent();\r
5009       /*\r
5010        * refresh the tags dialog only if it's visible\r
5011        */\r
5012       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5013           char *tags;\r
5014           tags = PGNTags(&gameInfo);\r
5015           TagsPopUp(tags, CmailMsg());\r
5016           free(tags);\r
5017       }\r
5018       SAY("computer starts playing white");\r
5019       break;\r
5020 \r
5021     case IDM_MachineBlack:\r
5022       MachineBlackEvent();\r
5023       /*\r
5024        * refresh the tags dialog only if it's visible\r
5025        */\r
5026       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5027           char *tags;\r
5028           tags = PGNTags(&gameInfo);\r
5029           TagsPopUp(tags, CmailMsg());\r
5030           free(tags);\r
5031       }\r
5032       SAY("computer starts playing black");\r
5033       break;\r
5034 \r
5035     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5036       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5037       break;\r
5038 \r
5039     case IDM_TwoMachines:\r
5040       TwoMachinesEvent();\r
5041       /*\r
5042        * refresh the tags dialog only if it's visible\r
5043        */\r
5044       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5045           char *tags;\r
5046           tags = PGNTags(&gameInfo);\r
5047           TagsPopUp(tags, CmailMsg());\r
5048           free(tags);\r
5049       }\r
5050       SAY("computer starts playing both sides");\r
5051       break;\r
5052 \r
5053     case IDM_AnalysisMode:\r
5054       if(AnalyzeModeEvent()) {\r
5055         SAY("analyzing current position");\r
5056       }\r
5057       break;\r
5058 \r
5059     case IDM_AnalyzeFile:\r
5060       AnalyzeFileEvent();\r
5061       break;\r
5062 \r
5063     case IDM_IcsClient:\r
5064       IcsClientEvent();\r
5065       break;\r
5066 \r
5067     case IDM_EditGame:\r
5068     case IDM_EditGame2:\r
5069       EditGameEvent();\r
5070       SAY("edit game");\r
5071       break;\r
5072 \r
5073     case IDM_EditPosition:\r
5074     case IDM_EditPosition2:\r
5075       EditPositionEvent();\r
5076       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5077       break;\r
5078 \r
5079     case IDM_Training:\r
5080       TrainingEvent();\r
5081       break;\r
5082 \r
5083     case IDM_ShowGameList:\r
5084       ShowGameListProc();\r
5085       break;\r
5086 \r
5087     case IDM_EditProgs1:\r
5088       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5089       break;\r
5090 \r
5091     case IDM_LoadProg1:\r
5092      LoadEnginePopUp(hwndMain, 0);\r
5093       break;\r
5094 \r
5095     case IDM_LoadProg2:\r
5096      LoadEnginePopUp(hwndMain, 1);\r
5097       break;\r
5098 \r
5099     case IDM_EditServers:\r
5100       EditTagsPopUp(icsNames, &icsNames);\r
5101       break;\r
5102 \r
5103     case IDM_EditTags:\r
5104     case IDM_Tags:\r
5105       EditTagsProc();\r
5106       break;\r
5107 \r
5108     case IDM_EditBook:\r
5109       EditBookEvent();\r
5110       break;\r
5111 \r
5112     case IDM_EditComment:\r
5113     case IDM_Comment:\r
5114       if (commentUp && editComment) {\r
5115         CommentPopDown();\r
5116       } else {\r
5117         EditCommentEvent();\r
5118       }\r
5119       break;\r
5120 \r
5121     case IDM_Pause:\r
5122       PauseEvent();\r
5123       break;\r
5124 \r
5125     case IDM_Accept:\r
5126       AcceptEvent();\r
5127       break;\r
5128 \r
5129     case IDM_Decline:\r
5130       DeclineEvent();\r
5131       break;\r
5132 \r
5133     case IDM_Rematch:\r
5134       RematchEvent();\r
5135       break;\r
5136 \r
5137     case IDM_CallFlag:\r
5138       CallFlagEvent();\r
5139       break;\r
5140 \r
5141     case IDM_Draw:\r
5142       DrawEvent();\r
5143       break;\r
5144 \r
5145     case IDM_Adjourn:\r
5146       AdjournEvent();\r
5147       break;\r
5148 \r
5149     case IDM_Abort:\r
5150       AbortEvent();\r
5151       break;\r
5152 \r
5153     case IDM_Resign:\r
5154       ResignEvent();\r
5155       break;\r
5156 \r
5157     case IDM_StopObserving:\r
5158       StopObservingEvent();\r
5159       break;\r
5160 \r
5161     case IDM_StopExamining:\r
5162       StopExaminingEvent();\r
5163       break;\r
5164 \r
5165     case IDM_Upload:\r
5166       UploadGameEvent();\r
5167       break;\r
5168 \r
5169     case IDM_TypeInMove:\r
5170       TypeInEvent('\000');\r
5171       break;\r
5172 \r
5173     case IDM_TypeInName:\r
5174       PopUpNameDialog('\000');\r
5175       break;\r
5176 \r
5177     case IDM_Backward:\r
5178       BackwardEvent();\r
5179       SetFocus(hwndMain);\r
5180       break;\r
5181 \r
5182     JAWS_MENU_ITEMS\r
5183 \r
5184     case IDM_Forward:\r
5185       ForwardEvent();\r
5186       SetFocus(hwndMain);\r
5187       break;\r
5188 \r
5189     case IDM_ToStart:\r
5190       ToStartEvent();\r
5191       SetFocus(hwndMain);\r
5192       break;\r
5193 \r
5194     case IDM_ToEnd:\r
5195       ToEndEvent();\r
5196       SetFocus(hwndMain);\r
5197       break;\r
5198 \r
5199     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5200     case OPT_GameListPrev:\r
5201       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5202       break;\r
5203 \r
5204     case IDM_Revert:\r
5205       RevertEvent(FALSE);\r
5206       break;\r
5207 \r
5208     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5209       RevertEvent(TRUE);\r
5210       break;\r
5211 \r
5212     case IDM_TruncateGame:\r
5213       TruncateGameEvent();\r
5214       break;\r
5215 \r
5216     case IDM_MoveNow:\r
5217       MoveNowEvent();\r
5218       break;\r
5219 \r
5220     case IDM_RetractMove:\r
5221       RetractMoveEvent();\r
5222       break;\r
5223 \r
5224     case IDM_FlipView:\r
5225       flipView = !flipView;\r
5226       DrawPosition(FALSE, NULL);\r
5227       break;\r
5228 \r
5229     case IDM_FlipClock:\r
5230       flipClock = !flipClock;\r
5231       DisplayBothClocks();\r
5232       DisplayLogos();\r
5233       break;\r
5234 \r
5235     case IDM_MuteSounds:\r
5236       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5237       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5238                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5239       break;\r
5240 \r
5241     case IDM_GeneralOptions:\r
5242       GeneralOptionsPopup(hwnd);\r
5243       DrawPosition(TRUE, NULL);\r
5244       break;\r
5245 \r
5246     case IDM_BoardOptions:\r
5247       BoardOptionsPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_ThemeOptions:\r
5251       ThemeOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_EnginePlayOptions:\r
5255       EnginePlayOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_Engine1Options:\r
5259       EngineOptionsPopup(hwnd, &first);\r
5260       break;\r
5261 \r
5262     case IDM_Engine2Options:\r
5263       savedHwnd = hwnd;\r
5264       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5265       EngineOptionsPopup(hwnd, &second);\r
5266       break;\r
5267 \r
5268     case IDM_OptionsUCI:\r
5269       UciOptionsPopup(hwnd);\r
5270       break;\r
5271 \r
5272     case IDM_Tourney:\r
5273       TourneyPopup(hwnd);\r
5274       break;\r
5275 \r
5276     case IDM_IcsOptions:\r
5277       IcsOptionsPopup(hwnd);\r
5278       break;\r
5279 \r
5280     case IDM_Fonts:\r
5281       FontsOptionsPopup(hwnd);\r
5282       break;\r
5283 \r
5284     case IDM_Sounds:\r
5285       SoundOptionsPopup(hwnd);\r
5286       break;\r
5287 \r
5288     case IDM_CommPort:\r
5289       CommPortOptionsPopup(hwnd);\r
5290       break;\r
5291 \r
5292     case IDM_LoadOptions:\r
5293       LoadOptionsPopup(hwnd);\r
5294       break;\r
5295 \r
5296     case IDM_SaveOptions:\r
5297       SaveOptionsPopup(hwnd);\r
5298       break;\r
5299 \r
5300     case IDM_TimeControl:\r
5301       TimeControlOptionsPopup(hwnd);\r
5302       break;\r
5303 \r
5304     case IDM_SaveSettings:\r
5305       SaveSettings(settingsFileName);\r
5306       break;\r
5307 \r
5308     case IDM_SaveSettingsOnExit:\r
5309       saveSettingsOnExit = !saveSettingsOnExit;\r
5310       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5311                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5312                                          MF_CHECKED : MF_UNCHECKED));\r
5313       break;\r
5314 \r
5315     case IDM_Hint:\r
5316       HintEvent();\r
5317       break;\r
5318 \r
5319     case IDM_Book:\r
5320       BookEvent();\r
5321       break;\r
5322 \r
5323     case IDM_AboutGame:\r
5324       AboutGameEvent();\r
5325       break;\r
5326 \r
5327     case IDM_Debug:\r
5328       appData.debugMode = !appData.debugMode;\r
5329       if (appData.debugMode) {\r
5330         char dir[MSG_SIZ];\r
5331         GetCurrentDirectory(MSG_SIZ, dir);\r
5332         SetCurrentDirectory(installDir);\r
5333         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5334         SetCurrentDirectory(dir);\r
5335         setbuf(debugFP, NULL);\r
5336       } else {\r
5337         fclose(debugFP);\r
5338         debugFP = NULL;\r
5339       }\r
5340       break;\r
5341 \r
5342     case IDM_HELPCONTENTS:\r
5343       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5344           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5345           MessageBox (GetFocus(),\r
5346                     _("Unable to activate help"),\r
5347                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5348       }\r
5349       break;\r
5350 \r
5351     case IDM_HELPSEARCH:\r
5352         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5353             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5354         MessageBox (GetFocus(),\r
5355                     _("Unable to activate help"),\r
5356                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5357       }\r
5358       break;\r
5359 \r
5360     case IDM_HELPHELP:\r
5361       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5362         MessageBox (GetFocus(),\r
5363                     _("Unable to activate help"),\r
5364                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5365       }\r
5366       break;\r
5367 \r
5368     case IDM_ABOUT:\r
5369       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5370       DialogBox(hInst, \r
5371         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5372         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5373       FreeProcInstance(lpProc);\r
5374       break;\r
5375 \r
5376     case IDM_DirectCommand1:\r
5377       AskQuestionEvent(_("Direct Command"),\r
5378                        _("Send to chess program:"), "", "1");\r
5379       break;\r
5380     case IDM_DirectCommand2:\r
5381       AskQuestionEvent(_("Direct Command"),\r
5382                        _("Send to second chess program:"), "", "2");\r
5383       break;\r
5384 \r
5385     case EP_WhitePawn:\r
5386       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case EP_WhiteKnight:\r
5391       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5392       fromX = fromY = -1;\r
5393       break;\r
5394 \r
5395     case EP_WhiteBishop:\r
5396       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case EP_WhiteRook:\r
5401       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5402       fromX = fromY = -1;\r
5403       break;\r
5404 \r
5405     case EP_WhiteQueen:\r
5406       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5407       fromX = fromY = -1;\r
5408       break;\r
5409 \r
5410     case EP_WhiteFerz:\r
5411       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5412       fromX = fromY = -1;\r
5413       break;\r
5414 \r
5415     case EP_WhiteWazir:\r
5416       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_WhiteAlfil:\r
5421       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case EP_WhiteCannon:\r
5426       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case EP_WhiteCardinal:\r
5431       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case EP_WhiteMarshall:\r
5436       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_WhiteKing:\r
5441       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_BlackPawn:\r
5446       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_BlackKnight:\r
5451       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_BlackBishop:\r
5456       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_BlackRook:\r
5461       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_BlackQueen:\r
5466       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_BlackFerz:\r
5471       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_BlackWazir:\r
5476       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_BlackAlfil:\r
5481       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_BlackCannon:\r
5486       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_BlackCardinal:\r
5491       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_BlackMarshall:\r
5496       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_BlackKing:\r
5501       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_EmptySquare:\r
5506       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_ClearBoard:\r
5511       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_White:\r
5516       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_Black:\r
5521       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_Promote:\r
5526       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_Demote:\r
5531       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case DP_Pawn:\r
5536       DropMenuEvent(WhitePawn, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case DP_Knight:\r
5541       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case DP_Bishop:\r
5546       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case DP_Rook:\r
5551       DropMenuEvent(WhiteRook, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case DP_Queen:\r
5556       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case IDM_English:\r
5561       barbaric = 0; appData.language = "";\r
5562       TranslateMenus(0);\r
5563       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5564       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5565       lastChecked = wmId;\r
5566       break;\r
5567 \r
5568     default:\r
5569       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5570           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5571       else\r
5572       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5573           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5574           TranslateMenus(0);\r
5575           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5576           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5577           lastChecked = wmId;\r
5578           break;\r
5579       }\r
5580       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5581     }\r
5582     break;\r
5583 \r
5584   case WM_TIMER:\r
5585     switch (wParam) {\r
5586     case CLOCK_TIMER_ID:\r
5587       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5588       clockTimerEvent = 0;\r
5589       DecrementClocks(); /* call into back end */\r
5590       break;\r
5591     case LOAD_GAME_TIMER_ID:\r
5592       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5593       loadGameTimerEvent = 0;\r
5594       AutoPlayGameLoop(); /* call into back end */\r
5595       break;\r
5596     case ANALYSIS_TIMER_ID:\r
5597       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5598                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5599         AnalysisPeriodicEvent(0);\r
5600       } else {\r
5601         KillTimer(hwnd, analysisTimerEvent);\r
5602         analysisTimerEvent = 0;\r
5603       }\r
5604       break;\r
5605     case DELAYED_TIMER_ID:\r
5606       KillTimer(hwnd, delayedTimerEvent);\r
5607       delayedTimerEvent = 0;\r
5608       delayedTimerCallback();\r
5609       break;\r
5610     }\r
5611     break;\r
5612 \r
5613   case WM_USER_Input:\r
5614     InputEvent(hwnd, message, wParam, lParam);\r
5615     break;\r
5616 \r
5617   /* [AS] Also move "attached" child windows */\r
5618   case WM_WINDOWPOSCHANGING:\r
5619 \r
5620     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5621         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5622 \r
5623         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5624             /* Window is moving */\r
5625             RECT rcMain;\r
5626 \r
5627 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5628             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5629             rcMain.right  = wpMain.x + wpMain.width;\r
5630             rcMain.top    = wpMain.y;\r
5631             rcMain.bottom = wpMain.y + wpMain.height;\r
5632             \r
5633             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5634             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5635             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5636             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5637             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5638             wpMain.x = lpwp->x;\r
5639             wpMain.y = lpwp->y;\r
5640         }\r
5641     }\r
5642     break;\r
5643 \r
5644   /* [AS] Snapping */\r
5645   case WM_ENTERSIZEMOVE:\r
5646     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5647     if (hwnd == hwndMain) {\r
5648       doingSizing = TRUE;\r
5649       lastSizing = 0;\r
5650     }\r
5651     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5652     break;\r
5653 \r
5654   case WM_SIZING:\r
5655     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5656     if (hwnd == hwndMain) {\r
5657       lastSizing = wParam;\r
5658     }\r
5659     break;\r
5660 \r
5661   case WM_MOVING:\r
5662     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5663       return OnMoving( &sd, hwnd, wParam, lParam );\r
5664 \r
5665   case WM_EXITSIZEMOVE:\r
5666     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5667     if (hwnd == hwndMain) {\r
5668       RECT client;\r
5669       doingSizing = FALSE;\r
5670       InvalidateRect(hwnd, &boardRect, FALSE);\r
5671       GetClientRect(hwnd, &client);\r
5672       ResizeBoard(client.right, client.bottom, lastSizing);\r
5673       lastSizing = 0;\r
5674       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5675     }\r
5676     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5677     break;\r
5678 \r
5679   case WM_DESTROY: /* message: window being destroyed */\r
5680     PostQuitMessage(0);\r
5681     break;\r
5682 \r
5683   case WM_CLOSE:\r
5684     if (hwnd == hwndMain) {\r
5685       ExitEvent(0);\r
5686     }\r
5687     break;\r
5688 \r
5689   default:      /* Passes it on if unprocessed */\r
5690     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5691   }\r
5692   return 0;\r
5693 }\r
5694 \r
5695 /*---------------------------------------------------------------------------*\\r
5696  *\r
5697  * Misc utility routines\r
5698  *\r
5699 \*---------------------------------------------------------------------------*/\r
5700 \r
5701 /*\r
5702  * Decent random number generator, at least not as bad as Windows\r
5703  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5704  */\r
5705 unsigned int randstate;\r
5706 \r
5707 int\r
5708 myrandom(void)\r
5709 {\r
5710   randstate = randstate * 1664525 + 1013904223;\r
5711   return (int) randstate & 0x7fffffff;\r
5712 }\r
5713 \r
5714 void\r
5715 mysrandom(unsigned int seed)\r
5716 {\r
5717   randstate = seed;\r
5718 }\r
5719 \r
5720 \r
5721 /* \r
5722  * returns TRUE if user selects a different color, FALSE otherwise \r
5723  */\r
5724 \r
5725 BOOL\r
5726 ChangeColor(HWND hwnd, COLORREF *which)\r
5727 {\r
5728   static BOOL firstTime = TRUE;\r
5729   static DWORD customColors[16];\r
5730   CHOOSECOLOR cc;\r
5731   COLORREF newcolor;\r
5732   int i;\r
5733   ColorClass ccl;\r
5734 \r
5735   if (firstTime) {\r
5736     /* Make initial colors in use available as custom colors */\r
5737     /* Should we put the compiled-in defaults here instead? */\r
5738     i = 0;\r
5739     customColors[i++] = lightSquareColor & 0xffffff;\r
5740     customColors[i++] = darkSquareColor & 0xffffff;\r
5741     customColors[i++] = whitePieceColor & 0xffffff;\r
5742     customColors[i++] = blackPieceColor & 0xffffff;\r
5743     customColors[i++] = highlightSquareColor & 0xffffff;\r
5744     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5745 \r
5746     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5747       customColors[i++] = textAttribs[ccl].color;\r
5748     }\r
5749     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5750     firstTime = FALSE;\r
5751   }\r
5752 \r
5753   cc.lStructSize = sizeof(cc);\r
5754   cc.hwndOwner = hwnd;\r
5755   cc.hInstance = NULL;\r
5756   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5757   cc.lpCustColors = (LPDWORD) customColors;\r
5758   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5759 \r
5760   if (!ChooseColor(&cc)) return FALSE;\r
5761 \r
5762   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5763   if (newcolor == *which) return FALSE;\r
5764   *which = newcolor;\r
5765   return TRUE;\r
5766 \r
5767   /*\r
5768   InitDrawingColors();\r
5769   InvalidateRect(hwnd, &boardRect, FALSE);\r
5770   */\r
5771 }\r
5772 \r
5773 BOOLEAN\r
5774 MyLoadSound(MySound *ms)\r
5775 {\r
5776   BOOL ok = FALSE;\r
5777   struct stat st;\r
5778   FILE *f;\r
5779 \r
5780   if (ms->data && ms->flag) free(ms->data);\r
5781   ms->data = NULL;\r
5782 \r
5783   switch (ms->name[0]) {\r
5784   case NULLCHAR:\r
5785     /* Silence */\r
5786     ok = TRUE;\r
5787     break;\r
5788   case '$':\r
5789     /* System sound from Control Panel.  Don't preload here. */\r
5790     ok = TRUE;\r
5791     break;\r
5792   case '!':\r
5793     if (ms->name[1] == NULLCHAR) {\r
5794       /* "!" alone = silence */\r
5795       ok = TRUE;\r
5796     } else {\r
5797       /* Builtin wave resource.  Error if not found. */\r
5798       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5799       if (h == NULL) break;\r
5800       ms->data = (void *)LoadResource(hInst, h);\r
5801       ms->flag = 0; // not maloced, so cannot be freed!\r
5802       if (h == NULL) break;\r
5803       ok = TRUE;\r
5804     }\r
5805     break;\r
5806   default:\r
5807     /* .wav file.  Error if not found. */\r
5808     f = fopen(ms->name, "rb");\r
5809     if (f == NULL) break;\r
5810     if (fstat(fileno(f), &st) < 0) break;\r
5811     ms->data = malloc(st.st_size);\r
5812     ms->flag = 1;\r
5813     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5814     fclose(f);\r
5815     ok = TRUE;\r
5816     break;\r
5817   }\r
5818   if (!ok) {\r
5819     char buf[MSG_SIZ];\r
5820       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5821     DisplayError(buf, GetLastError());\r
5822   }\r
5823   return ok;\r
5824 }\r
5825 \r
5826 BOOLEAN\r
5827 MyPlaySound(MySound *ms)\r
5828 {\r
5829   BOOLEAN ok = FALSE;\r
5830 \r
5831   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5832   switch (ms->name[0]) {\r
5833   case NULLCHAR:\r
5834         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5835     /* Silence */\r
5836     ok = TRUE;\r
5837     break;\r
5838   case '$':\r
5839     /* System sound from Control Panel (deprecated feature).\r
5840        "$" alone or an unset sound name gets default beep (still in use). */\r
5841     if (ms->name[1]) {\r
5842       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5843     }\r
5844     if (!ok) ok = MessageBeep(MB_OK);\r
5845     break; \r
5846   case '!':\r
5847     /* Builtin wave resource, or "!" alone for silence */\r
5848     if (ms->name[1]) {\r
5849       if (ms->data == NULL) return FALSE;\r
5850       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5851     } else {\r
5852       ok = TRUE;\r
5853     }\r
5854     break;\r
5855   default:\r
5856     /* .wav file.  Error if not found. */\r
5857     if (ms->data == NULL) return FALSE;\r
5858     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5859     break;\r
5860   }\r
5861   /* Don't print an error: this can happen innocently if the sound driver\r
5862      is busy; for instance, if another instance of WinBoard is playing\r
5863      a sound at about the same time. */\r
5864   return ok;\r
5865 }\r
5866 \r
5867 \r
5868 LRESULT CALLBACK\r
5869 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5870 {\r
5871   BOOL ok;\r
5872   OPENFILENAME *ofn;\r
5873   static UINT *number; /* gross that this is static */\r
5874 \r
5875   switch (message) {\r
5876   case WM_INITDIALOG: /* message: initialize dialog box */\r
5877     /* Center the dialog over the application window */\r
5878     ofn = (OPENFILENAME *) lParam;\r
5879     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5880       number = (UINT *) ofn->lCustData;\r
5881       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5882     } else {\r
5883       number = NULL;\r
5884     }\r
5885     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5886     Translate(hDlg, 1536);\r
5887     return FALSE;  /* Allow for further processing */\r
5888 \r
5889   case WM_COMMAND:\r
5890     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5891       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5892     }\r
5893     return FALSE;  /* Allow for further processing */\r
5894   }\r
5895   return FALSE;\r
5896 }\r
5897 \r
5898 UINT APIENTRY\r
5899 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5900 {\r
5901   static UINT *number;\r
5902   OPENFILENAME *ofname;\r
5903   OFNOTIFY *ofnot;\r
5904   switch (uiMsg) {\r
5905   case WM_INITDIALOG:\r
5906     Translate(hdlg, DLG_IndexNumber);\r
5907     ofname = (OPENFILENAME *)lParam;\r
5908     number = (UINT *)(ofname->lCustData);\r
5909     break;\r
5910   case WM_NOTIFY:\r
5911     ofnot = (OFNOTIFY *)lParam;\r
5912     if (ofnot->hdr.code == CDN_FILEOK) {\r
5913       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5914     }\r
5915     break;\r
5916   }\r
5917   return 0;\r
5918 }\r
5919 \r
5920 \r
5921 FILE *\r
5922 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5923                char *nameFilt, char *dlgTitle, UINT *number,\r
5924                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5925 {\r
5926   OPENFILENAME openFileName;\r
5927   char buf1[MSG_SIZ];\r
5928   FILE *f;\r
5929 \r
5930   if (fileName == NULL) fileName = buf1;\r
5931   if (defName == NULL) {\r
5932     safeStrCpy(fileName, "*.", 3 );\r
5933     strcat(fileName, defExt);\r
5934   } else {\r
5935     safeStrCpy(fileName, defName, MSG_SIZ );\r
5936   }\r
5937     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5938   if (number) *number = 0;\r
5939 \r
5940   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5941   openFileName.hwndOwner         = hwnd;\r
5942   openFileName.hInstance         = (HANDLE) hInst;\r
5943   openFileName.lpstrFilter       = nameFilt;\r
5944   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5945   openFileName.nMaxCustFilter    = 0L;\r
5946   openFileName.nFilterIndex      = 1L;\r
5947   openFileName.lpstrFile         = fileName;\r
5948   openFileName.nMaxFile          = MSG_SIZ;\r
5949   openFileName.lpstrFileTitle    = fileTitle;\r
5950   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5951   openFileName.lpstrInitialDir   = NULL;\r
5952   openFileName.lpstrTitle        = dlgTitle;\r
5953   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5954     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5955     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5956     | (oldDialog ? 0 : OFN_EXPLORER);\r
5957   openFileName.nFileOffset       = 0;\r
5958   openFileName.nFileExtension    = 0;\r
5959   openFileName.lpstrDefExt       = defExt;\r
5960   openFileName.lCustData         = (LONG) number;\r
5961   openFileName.lpfnHook          = oldDialog ?\r
5962     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5963   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5964 \r
5965   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5966                         GetOpenFileName(&openFileName)) {\r
5967     /* open the file */\r
5968     f = fopen(openFileName.lpstrFile, write);\r
5969     if (f == NULL) {\r
5970       MessageBox(hwnd, _("File open failed"), NULL,\r
5971                  MB_OK|MB_ICONEXCLAMATION);\r
5972       return NULL;\r
5973     }\r
5974   } else {\r
5975     int err = CommDlgExtendedError();\r
5976     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5977     return FALSE;\r
5978   }\r
5979   return f;\r
5980 }\r
5981 \r
5982 \r
5983 \r
5984 VOID APIENTRY\r
5985 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5986 {\r
5987   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5988 \r
5989   /*\r
5990    * Get the first pop-up menu in the menu template. This is the\r
5991    * menu that TrackPopupMenu displays.\r
5992    */\r
5993   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5994   TranslateOneMenu(10, hmenuTrackPopup);\r
5995 \r
5996   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5997 \r
5998   /*\r
5999    * TrackPopup uses screen coordinates, so convert the\r
6000    * coordinates of the mouse click to screen coordinates.\r
6001    */\r
6002   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6003 \r
6004   /* Draw and track the floating pop-up menu. */\r
6005   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6006                  pt.x, pt.y, 0, hwnd, NULL);\r
6007 \r
6008   /* Destroy the menu.*/\r
6009   DestroyMenu(hmenu);\r
6010 }\r
6011    \r
6012 typedef struct {\r
6013   HWND hDlg, hText;\r
6014   int sizeX, sizeY, newSizeX, newSizeY;\r
6015   HDWP hdwp;\r
6016 } ResizeEditPlusButtonsClosure;\r
6017 \r
6018 BOOL CALLBACK\r
6019 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6020 {\r
6021   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6022   RECT rect;\r
6023   POINT pt;\r
6024 \r
6025   if (hChild == cl->hText) return TRUE;\r
6026   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6027   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6028   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6029   ScreenToClient(cl->hDlg, &pt);\r
6030   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6031     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6032   return TRUE;\r
6033 }\r
6034 \r
6035 /* Resize a dialog that has a (rich) edit field filling most of\r
6036    the top, with a row of buttons below */\r
6037 VOID\r
6038 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6039 {\r
6040   RECT rectText;\r
6041   int newTextHeight, newTextWidth;\r
6042   ResizeEditPlusButtonsClosure cl;\r
6043   \r
6044   /*if (IsIconic(hDlg)) return;*/\r
6045   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6046   \r
6047   cl.hdwp = BeginDeferWindowPos(8);\r
6048 \r
6049   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6050   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6051   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6052   if (newTextHeight < 0) {\r
6053     newSizeY += -newTextHeight;\r
6054     newTextHeight = 0;\r
6055   }\r
6056   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6057     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6058 \r
6059   cl.hDlg = hDlg;\r
6060   cl.hText = hText;\r
6061   cl.sizeX = sizeX;\r
6062   cl.sizeY = sizeY;\r
6063   cl.newSizeX = newSizeX;\r
6064   cl.newSizeY = newSizeY;\r
6065   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6066 \r
6067   EndDeferWindowPos(cl.hdwp);\r
6068 }\r
6069 \r
6070 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6071 {\r
6072     RECT    rChild, rParent;\r
6073     int     wChild, hChild, wParent, hParent;\r
6074     int     wScreen, hScreen, xNew, yNew;\r
6075     HDC     hdc;\r
6076 \r
6077     /* Get the Height and Width of the child window */\r
6078     GetWindowRect (hwndChild, &rChild);\r
6079     wChild = rChild.right - rChild.left;\r
6080     hChild = rChild.bottom - rChild.top;\r
6081 \r
6082     /* Get the Height and Width of the parent window */\r
6083     GetWindowRect (hwndParent, &rParent);\r
6084     wParent = rParent.right - rParent.left;\r
6085     hParent = rParent.bottom - rParent.top;\r
6086 \r
6087     /* Get the display limits */\r
6088     hdc = GetDC (hwndChild);\r
6089     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6090     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6091     ReleaseDC(hwndChild, hdc);\r
6092 \r
6093     /* Calculate new X position, then adjust for screen */\r
6094     xNew = rParent.left + ((wParent - wChild) /2);\r
6095     if (xNew < 0) {\r
6096         xNew = 0;\r
6097     } else if ((xNew+wChild) > wScreen) {\r
6098         xNew = wScreen - wChild;\r
6099     }\r
6100 \r
6101     /* Calculate new Y position, then adjust for screen */\r
6102     if( mode == 0 ) {\r
6103         yNew = rParent.top  + ((hParent - hChild) /2);\r
6104     }\r
6105     else {\r
6106         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6107     }\r
6108 \r
6109     if (yNew < 0) {\r
6110         yNew = 0;\r
6111     } else if ((yNew+hChild) > hScreen) {\r
6112         yNew = hScreen - hChild;\r
6113     }\r
6114 \r
6115     /* Set it, and return */\r
6116     return SetWindowPos (hwndChild, NULL,\r
6117                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6118 }\r
6119 \r
6120 /* Center one window over another */\r
6121 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6122 {\r
6123     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6124 }\r
6125 \r
6126 /*---------------------------------------------------------------------------*\\r
6127  *\r
6128  * Startup Dialog functions\r
6129  *\r
6130 \*---------------------------------------------------------------------------*/\r
6131 void\r
6132 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6133 {\r
6134   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6135 \r
6136   while (*cd != NULL) {\r
6137     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6138     cd++;\r
6139   }\r
6140 }\r
6141 \r
6142 void\r
6143 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6144 {\r
6145   char buf1[MAX_ARG_LEN];\r
6146   int len;\r
6147 \r
6148   if (str[0] == '@') {\r
6149     FILE* f = fopen(str + 1, "r");\r
6150     if (f == NULL) {\r
6151       DisplayFatalError(str + 1, errno, 2);\r
6152       return;\r
6153     }\r
6154     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6155     fclose(f);\r
6156     buf1[len] = NULLCHAR;\r
6157     str = buf1;\r
6158   }\r
6159 \r
6160   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6161 \r
6162   for (;;) {\r
6163     char buf[MSG_SIZ];\r
6164     char *end = strchr(str, '\n');\r
6165     if (end == NULL) return;\r
6166     memcpy(buf, str, end - str);\r
6167     buf[end - str] = NULLCHAR;\r
6168     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6169     str = end + 1;\r
6170   }\r
6171 }\r
6172 \r
6173 void\r
6174 SetStartupDialogEnables(HWND hDlg)\r
6175 {\r
6176   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6177     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6178     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6179   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6180     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6181   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6182     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6183   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6184     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6185   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6186     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6187     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6188     IsDlgButtonChecked(hDlg, OPT_View));\r
6189 }\r
6190 \r
6191 char *\r
6192 QuoteForFilename(char *filename)\r
6193 {\r
6194   int dquote, space;\r
6195   dquote = strchr(filename, '"') != NULL;\r
6196   space = strchr(filename, ' ') != NULL;\r
6197   if (dquote || space) {\r
6198     if (dquote) {\r
6199       return "'";\r
6200     } else {\r
6201       return "\"";\r
6202     }\r
6203   } else {\r
6204     return "";\r
6205   }\r
6206 }\r
6207 \r
6208 VOID\r
6209 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6210 {\r
6211   char buf[MSG_SIZ];\r
6212   char *q;\r
6213 \r
6214   InitComboStringsFromOption(hwndCombo, nthnames);\r
6215   q = QuoteForFilename(nthcp);\r
6216     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6217   if (*nthdir != NULLCHAR) {\r
6218     q = QuoteForFilename(nthdir);\r
6219       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6220   }\r
6221   if (*nthcp == NULLCHAR) {\r
6222     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6223   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6224     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6225     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6226   }\r
6227 }\r
6228 \r
6229 LRESULT CALLBACK\r
6230 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6231 {\r
6232   char buf[MSG_SIZ];\r
6233   HANDLE hwndCombo;\r
6234   char *p;\r
6235 \r
6236   switch (message) {\r
6237   case WM_INITDIALOG:\r
6238     /* Center the dialog */\r
6239     CenterWindow (hDlg, GetDesktopWindow());\r
6240     Translate(hDlg, DLG_Startup);\r
6241     /* Initialize the dialog items */\r
6242     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6243                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6244                   firstChessProgramNames);\r
6245     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6246                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6247                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6248     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6249     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6250       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6251     if (*appData.icsHelper != NULLCHAR) {\r
6252       char *q = QuoteForFilename(appData.icsHelper);\r
6253       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6254     }\r
6255     if (*appData.icsHost == NULLCHAR) {\r
6256       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6257       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6258     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6259       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6260       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6261     }\r
6262 \r
6263     if (appData.icsActive) {\r
6264       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6265     }\r
6266     else if (appData.noChessProgram) {\r
6267       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6268     }\r
6269     else {\r
6270       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6271     }\r
6272 \r
6273     SetStartupDialogEnables(hDlg);\r
6274     return TRUE;\r
6275 \r
6276   case WM_COMMAND:\r
6277     switch (LOWORD(wParam)) {\r
6278     case IDOK:\r
6279       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6280         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6281         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6282         p = buf;\r
6283         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6284         ParseArgs(StringGet, &p);\r
6285         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6286         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6287         p = buf;\r
6288         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6289         ParseArgs(StringGet, &p);\r
6290         SwapEngines(singleList); // ... and then make it 'second'\r
6291         appData.noChessProgram = FALSE;\r
6292         appData.icsActive = FALSE;\r
6293       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6294         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6295         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6296         p = buf;\r
6297         ParseArgs(StringGet, &p);\r
6298         if (appData.zippyPlay) {\r
6299           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6300           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6301           p = buf;\r
6302           ParseArgs(StringGet, &p);\r
6303         }\r
6304       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6305         appData.noChessProgram = TRUE;\r
6306         appData.icsActive = FALSE;\r
6307       } else {\r
6308         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6309                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6310         return TRUE;\r
6311       }\r
6312       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6313         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6314         p = buf;\r
6315         ParseArgs(StringGet, &p);\r
6316       }\r
6317       EndDialog(hDlg, TRUE);\r
6318       return TRUE;\r
6319 \r
6320     case IDCANCEL:\r
6321       ExitEvent(0);\r
6322       return TRUE;\r
6323 \r
6324     case IDM_HELPCONTENTS:\r
6325       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6326         MessageBox (GetFocus(),\r
6327                     _("Unable to activate help"),\r
6328                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6329       }\r
6330       break;\r
6331 \r
6332     default:\r
6333       SetStartupDialogEnables(hDlg);\r
6334       break;\r
6335     }\r
6336     break;\r
6337   }\r
6338   return FALSE;\r
6339 }\r
6340 \r
6341 /*---------------------------------------------------------------------------*\\r
6342  *\r
6343  * About box dialog functions\r
6344  *\r
6345 \*---------------------------------------------------------------------------*/\r
6346 \r
6347 /* Process messages for "About" dialog box */\r
6348 LRESULT CALLBACK\r
6349 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6350 {\r
6351   switch (message) {\r
6352   case WM_INITDIALOG: /* message: initialize dialog box */\r
6353     /* Center the dialog over the application window */\r
6354     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6355     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6356     Translate(hDlg, ABOUTBOX);\r
6357     JAWS_COPYRIGHT\r
6358     return (TRUE);\r
6359 \r
6360   case WM_COMMAND: /* message: received a command */\r
6361     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6362         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6363       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6364       return (TRUE);\r
6365     }\r
6366     break;\r
6367   }\r
6368   return (FALSE);\r
6369 }\r
6370 \r
6371 /*---------------------------------------------------------------------------*\\r
6372  *\r
6373  * Comment Dialog functions\r
6374  *\r
6375 \*---------------------------------------------------------------------------*/\r
6376 \r
6377 LRESULT CALLBACK\r
6378 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6379 {\r
6380   static HANDLE hwndText = NULL;\r
6381   int len, newSizeX, newSizeY, flags;\r
6382   static int sizeX, sizeY;\r
6383   char *str;\r
6384   RECT rect;\r
6385   MINMAXINFO *mmi;\r
6386 \r
6387   switch (message) {\r
6388   case WM_INITDIALOG: /* message: initialize dialog box */\r
6389     /* Initialize the dialog items */\r
6390     Translate(hDlg, DLG_EditComment);\r
6391     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6392     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6393     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6394     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6395     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6396     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6397     SetWindowText(hDlg, commentTitle);\r
6398     if (editComment) {\r
6399       SetFocus(hwndText);\r
6400     } else {\r
6401       SetFocus(GetDlgItem(hDlg, IDOK));\r
6402     }\r
6403     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6404                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6405                 MAKELPARAM(FALSE, 0));\r
6406     /* Size and position the dialog */\r
6407     if (!commentDialog) {\r
6408       commentDialog = hDlg;\r
6409       flags = SWP_NOZORDER;\r
6410       GetClientRect(hDlg, &rect);\r
6411       sizeX = rect.right;\r
6412       sizeY = rect.bottom;\r
6413       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6414           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6415         WINDOWPLACEMENT wp;\r
6416         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6417         wp.length = sizeof(WINDOWPLACEMENT);\r
6418         wp.flags = 0;\r
6419         wp.showCmd = SW_SHOW;\r
6420         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6421         wp.rcNormalPosition.left = wpComment.x;\r
6422         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6423         wp.rcNormalPosition.top = wpComment.y;\r
6424         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6425         SetWindowPlacement(hDlg, &wp);\r
6426 \r
6427         GetClientRect(hDlg, &rect);\r
6428         newSizeX = rect.right;\r
6429         newSizeY = rect.bottom;\r
6430         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6431                               newSizeX, newSizeY);\r
6432         sizeX = newSizeX;\r
6433         sizeY = newSizeY;\r
6434       }\r
6435     }\r
6436     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6437     return FALSE;\r
6438 \r
6439   case WM_COMMAND: /* message: received a command */\r
6440     switch (LOWORD(wParam)) {\r
6441     case IDOK:\r
6442       if (editComment) {\r
6443         char *p, *q;\r
6444         /* Read changed options from the dialog box */\r
6445         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6446         len = GetWindowTextLength(hwndText);\r
6447         str = (char *) malloc(len + 1);\r
6448         GetWindowText(hwndText, str, len + 1);\r
6449         p = q = str;\r
6450         while (*q) {\r
6451           if (*q == '\r')\r
6452             q++;\r
6453           else\r
6454             *p++ = *q++;\r
6455         }\r
6456         *p = NULLCHAR;\r
6457         ReplaceComment(commentIndex, str);\r
6458         free(str);\r
6459       }\r
6460       CommentPopDown();\r
6461       return TRUE;\r
6462 \r
6463     case IDCANCEL:\r
6464     case OPT_CancelComment:\r
6465       CommentPopDown();\r
6466       return TRUE;\r
6467 \r
6468     case OPT_ClearComment:\r
6469       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6470       break;\r
6471 \r
6472     case OPT_EditComment:\r
6473       EditCommentEvent();\r
6474       return TRUE;\r
6475 \r
6476     default:\r
6477       break;\r
6478     }\r
6479     break;\r
6480 \r
6481   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6482         if( wParam == OPT_CommentText ) {\r
6483             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6484 \r
6485             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6486                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6487                 POINTL pt;\r
6488                 LRESULT index;\r
6489 \r
6490                 pt.x = LOWORD( lpMF->lParam );\r
6491                 pt.y = HIWORD( lpMF->lParam );\r
6492 \r
6493                 if(lpMF->msg == WM_CHAR) {\r
6494                         CHARRANGE sel;\r
6495                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6496                         index = sel.cpMin;\r
6497                 } else\r
6498                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6499 \r
6500                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6501                 len = GetWindowTextLength(hwndText);\r
6502                 str = (char *) malloc(len + 1);\r
6503                 GetWindowText(hwndText, str, len + 1);\r
6504                 ReplaceComment(commentIndex, str);\r
6505                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6506                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6507                 free(str);\r
6508 \r
6509                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6510                 lpMF->msg = WM_USER;\r
6511 \r
6512                 return TRUE;\r
6513             }\r
6514         }\r
6515         break;\r
6516 \r
6517   case WM_SIZE:\r
6518     newSizeX = LOWORD(lParam);\r
6519     newSizeY = HIWORD(lParam);\r
6520     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6521     sizeX = newSizeX;\r
6522     sizeY = newSizeY;\r
6523     break;\r
6524 \r
6525   case WM_GETMINMAXINFO:\r
6526     /* Prevent resizing window too small */\r
6527     mmi = (MINMAXINFO *) lParam;\r
6528     mmi->ptMinTrackSize.x = 100;\r
6529     mmi->ptMinTrackSize.y = 100;\r
6530     break;\r
6531   }\r
6532   return FALSE;\r
6533 }\r
6534 \r
6535 VOID\r
6536 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6537 {\r
6538   FARPROC lpProc;\r
6539   char *p, *q;\r
6540 \r
6541   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6542 \r
6543   if (str == NULL) str = "";\r
6544   p = (char *) malloc(2 * strlen(str) + 2);\r
6545   q = p;\r
6546   while (*str) {\r
6547     if (*str == '\n') *q++ = '\r';\r
6548     *q++ = *str++;\r
6549   }\r
6550   *q = NULLCHAR;\r
6551   if (commentText != NULL) free(commentText);\r
6552 \r
6553   commentIndex = index;\r
6554   commentTitle = title;\r
6555   commentText = p;\r
6556   editComment = edit;\r
6557 \r
6558   if (commentDialog) {\r
6559     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6560     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6561   } else {\r
6562     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6563     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6564                  hwndMain, (DLGPROC)lpProc);\r
6565     FreeProcInstance(lpProc);\r
6566   }\r
6567   commentUp = TRUE;\r
6568 }\r
6569 \r
6570 \r
6571 /*---------------------------------------------------------------------------*\\r
6572  *\r
6573  * Type-in move dialog functions\r
6574  * \r
6575 \*---------------------------------------------------------------------------*/\r
6576 \r
6577 LRESULT CALLBACK\r
6578 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6579 {\r
6580   char move[MSG_SIZ];\r
6581   HWND hInput;\r
6582 \r
6583   switch (message) {\r
6584   case WM_INITDIALOG:\r
6585     move[0] = (char) lParam;\r
6586     move[1] = NULLCHAR;\r
6587     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6588     Translate(hDlg, DLG_TypeInMove);\r
6589     hInput = GetDlgItem(hDlg, OPT_Move);\r
6590     SetWindowText(hInput, move);\r
6591     SetFocus(hInput);\r
6592     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6593     return FALSE;\r
6594 \r
6595   case WM_COMMAND:\r
6596     switch (LOWORD(wParam)) {\r
6597     case IDOK:\r
6598 \r
6599       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6600       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6601       TypeInDoneEvent(move);\r
6602       EndDialog(hDlg, TRUE);\r
6603       return TRUE;\r
6604     case IDCANCEL:\r
6605       EndDialog(hDlg, FALSE);\r
6606       return TRUE;\r
6607     default:\r
6608       break;\r
6609     }\r
6610     break;\r
6611   }\r
6612   return FALSE;\r
6613 }\r
6614 \r
6615 VOID\r
6616 PopUpMoveDialog(char firstchar)\r
6617 {\r
6618     FARPROC lpProc;\r
6619 \r
6620       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6621       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6622         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6623       FreeProcInstance(lpProc);\r
6624 }\r
6625 \r
6626 /*---------------------------------------------------------------------------*\\r
6627  *\r
6628  * Type-in name dialog functions\r
6629  * \r
6630 \*---------------------------------------------------------------------------*/\r
6631 \r
6632 LRESULT CALLBACK\r
6633 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6634 {\r
6635   char move[MSG_SIZ];\r
6636   HWND hInput;\r
6637 \r
6638   switch (message) {\r
6639   case WM_INITDIALOG:\r
6640     move[0] = (char) lParam;\r
6641     move[1] = NULLCHAR;\r
6642     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6643     Translate(hDlg, DLG_TypeInName);\r
6644     hInput = GetDlgItem(hDlg, OPT_Name);\r
6645     SetWindowText(hInput, move);\r
6646     SetFocus(hInput);\r
6647     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6648     return FALSE;\r
6649 \r
6650   case WM_COMMAND:\r
6651     switch (LOWORD(wParam)) {\r
6652     case IDOK:\r
6653       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6654       appData.userName = strdup(move);\r
6655       SetUserLogo();\r
6656       SetGameInfo();\r
6657       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6658         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6659         DisplayTitle(move);\r
6660       }\r
6661 \r
6662 \r
6663       EndDialog(hDlg, TRUE);\r
6664       return TRUE;\r
6665     case IDCANCEL:\r
6666       EndDialog(hDlg, FALSE);\r
6667       return TRUE;\r
6668     default:\r
6669       break;\r
6670     }\r
6671     break;\r
6672   }\r
6673   return FALSE;\r
6674 }\r
6675 \r
6676 VOID\r
6677 PopUpNameDialog(char firstchar)\r
6678 {\r
6679     FARPROC lpProc;\r
6680     \r
6681       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6682       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6683         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6684       FreeProcInstance(lpProc);\r
6685 }\r
6686 \r
6687 /*---------------------------------------------------------------------------*\\r
6688  *\r
6689  *  Error dialogs\r
6690  * \r
6691 \*---------------------------------------------------------------------------*/\r
6692 \r
6693 /* Nonmodal error box */\r
6694 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6695                              WPARAM wParam, LPARAM lParam);\r
6696 \r
6697 VOID\r
6698 ErrorPopUp(char *title, char *content)\r
6699 {\r
6700   FARPROC lpProc;\r
6701   char *p, *q;\r
6702   BOOLEAN modal = hwndMain == NULL;\r
6703 \r
6704   p = content;\r
6705   q = errorMessage;\r
6706   while (*p) {\r
6707     if (*p == '\n') {\r
6708       if (modal) {\r
6709         *q++ = ' ';\r
6710         p++;\r
6711       } else {\r
6712         *q++ = '\r';\r
6713         *q++ = *p++;\r
6714       }\r
6715     } else {\r
6716       *q++ = *p++;\r
6717     }\r
6718   }\r
6719   *q = NULLCHAR;\r
6720   strncpy(errorTitle, title, sizeof(errorTitle));\r
6721   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6722   \r
6723   if (modal) {\r
6724     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6725   } else {\r
6726     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6727     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6728                  hwndMain, (DLGPROC)lpProc);\r
6729     FreeProcInstance(lpProc);\r
6730   }\r
6731 }\r
6732 \r
6733 VOID\r
6734 ErrorPopDown()\r
6735 {\r
6736   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6737   if (errorDialog == NULL) return;\r
6738   DestroyWindow(errorDialog);\r
6739   errorDialog = NULL;\r
6740   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6741 }\r
6742 \r
6743 LRESULT CALLBACK\r
6744 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6745 {\r
6746   HANDLE hwndText;\r
6747   RECT rChild;\r
6748 \r
6749   switch (message) {\r
6750   case WM_INITDIALOG:\r
6751     GetWindowRect(hDlg, &rChild);\r
6752 \r
6753     /*\r
6754     SetWindowPos(hDlg, NULL, rChild.left,\r
6755       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6756       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6757     */\r
6758 \r
6759     /* \r
6760         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6761         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6762         and it doesn't work when you resize the dialog.\r
6763         For now, just give it a default position.\r
6764     */\r
6765     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6766     Translate(hDlg, DLG_Error);\r
6767 \r
6768     errorDialog = hDlg;\r
6769     SetWindowText(hDlg, errorTitle);\r
6770     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6771     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6772     return FALSE;\r
6773 \r
6774   case WM_COMMAND:\r
6775     switch (LOWORD(wParam)) {\r
6776     case IDOK:\r
6777     case IDCANCEL:\r
6778       if (errorDialog == hDlg) errorDialog = NULL;\r
6779       DestroyWindow(hDlg);\r
6780       return TRUE;\r
6781 \r
6782     default:\r
6783       break;\r
6784     }\r
6785     break;\r
6786   }\r
6787   return FALSE;\r
6788 }\r
6789 \r
6790 #ifdef GOTHIC\r
6791 HWND gothicDialog = NULL;\r
6792 \r
6793 LRESULT CALLBACK\r
6794 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6795 {\r
6796   HANDLE hwndText;\r
6797   RECT rChild;\r
6798   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6799 \r
6800   switch (message) {\r
6801   case WM_INITDIALOG:\r
6802     GetWindowRect(hDlg, &rChild);\r
6803 \r
6804     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6805                                                              SWP_NOZORDER);\r
6806 \r
6807     /* \r
6808         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6809         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6810         and it doesn't work when you resize the dialog.\r
6811         For now, just give it a default position.\r
6812     */\r
6813     gothicDialog = hDlg;\r
6814     SetWindowText(hDlg, errorTitle);\r
6815     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6816     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6817     return FALSE;\r
6818 \r
6819   case WM_COMMAND:\r
6820     switch (LOWORD(wParam)) {\r
6821     case IDOK:\r
6822     case IDCANCEL:\r
6823       if (errorDialog == hDlg) errorDialog = NULL;\r
6824       DestroyWindow(hDlg);\r
6825       return TRUE;\r
6826 \r
6827     default:\r
6828       break;\r
6829     }\r
6830     break;\r
6831   }\r
6832   return FALSE;\r
6833 }\r
6834 \r
6835 VOID\r
6836 GothicPopUp(char *title, VariantClass variant)\r
6837 {\r
6838   FARPROC lpProc;\r
6839   static char *lastTitle;\r
6840 \r
6841   strncpy(errorTitle, title, sizeof(errorTitle));\r
6842   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6843 \r
6844   if(lastTitle != title && gothicDialog != NULL) {\r
6845     DestroyWindow(gothicDialog);\r
6846     gothicDialog = NULL;\r
6847   }\r
6848   if(variant != VariantNormal && gothicDialog == NULL) {\r
6849     title = lastTitle;\r
6850     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6851     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6852                  hwndMain, (DLGPROC)lpProc);\r
6853     FreeProcInstance(lpProc);\r
6854   }\r
6855 }\r
6856 #endif\r
6857 \r
6858 /*---------------------------------------------------------------------------*\\r
6859  *\r
6860  *  Ics Interaction console functions\r
6861  *\r
6862 \*---------------------------------------------------------------------------*/\r
6863 \r
6864 #define HISTORY_SIZE 64\r
6865 static char *history[HISTORY_SIZE];\r
6866 int histIn = 0, histP = 0;\r
6867 \r
6868 VOID\r
6869 SaveInHistory(char *cmd)\r
6870 {\r
6871   if (history[histIn] != NULL) {\r
6872     free(history[histIn]);\r
6873     history[histIn] = NULL;\r
6874   }\r
6875   if (*cmd == NULLCHAR) return;\r
6876   history[histIn] = StrSave(cmd);\r
6877   histIn = (histIn + 1) % HISTORY_SIZE;\r
6878   if (history[histIn] != NULL) {\r
6879     free(history[histIn]);\r
6880     history[histIn] = NULL;\r
6881   }\r
6882   histP = histIn;\r
6883 }\r
6884 \r
6885 char *\r
6886 PrevInHistory(char *cmd)\r
6887 {\r
6888   int newhp;\r
6889   if (histP == histIn) {\r
6890     if (history[histIn] != NULL) free(history[histIn]);\r
6891     history[histIn] = StrSave(cmd);\r
6892   }\r
6893   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6894   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6895   histP = newhp;\r
6896   return history[histP];\r
6897 }\r
6898 \r
6899 char *\r
6900 NextInHistory()\r
6901 {\r
6902   if (histP == histIn) return NULL;\r
6903   histP = (histP + 1) % HISTORY_SIZE;\r
6904   return history[histP];   \r
6905 }\r
6906 \r
6907 HMENU\r
6908 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6909 {\r
6910   HMENU hmenu, h;\r
6911   int i = 0;\r
6912   hmenu = LoadMenu(hInst, "TextMenu");\r
6913   h = GetSubMenu(hmenu, 0);\r
6914   while (e->item) {\r
6915     if (strcmp(e->item, "-") == 0) {\r
6916       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6917     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6918       int flags = MF_STRING, j = 0;\r
6919       if (e->item[0] == '|') {\r
6920         flags |= MF_MENUBARBREAK;\r
6921         j++;\r
6922       }\r
6923       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6924       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6925     }\r
6926     e++;\r
6927     i++;\r
6928   } \r
6929   return hmenu;\r
6930 }\r
6931 \r
6932 WNDPROC consoleTextWindowProc;\r
6933 \r
6934 void\r
6935 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6936 {\r
6937   char buf[MSG_SIZ], name[MSG_SIZ];\r
6938   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6939   CHARRANGE sel;\r
6940 \r
6941   if (!getname) {\r
6942     SetWindowText(hInput, command);\r
6943     if (immediate) {\r
6944       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6945     } else {\r
6946       sel.cpMin = 999999;\r
6947       sel.cpMax = 999999;\r
6948       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6949       SetFocus(hInput);\r
6950     }\r
6951     return;\r
6952   }    \r
6953   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6954   if (sel.cpMin == sel.cpMax) {\r
6955     /* Expand to surrounding word */\r
6956     TEXTRANGE tr;\r
6957     do {\r
6958       tr.chrg.cpMax = sel.cpMin;\r
6959       tr.chrg.cpMin = --sel.cpMin;\r
6960       if (sel.cpMin < 0) break;\r
6961       tr.lpstrText = name;\r
6962       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6963     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6964     sel.cpMin++;\r
6965 \r
6966     do {\r
6967       tr.chrg.cpMin = sel.cpMax;\r
6968       tr.chrg.cpMax = ++sel.cpMax;\r
6969       tr.lpstrText = name;\r
6970       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6971     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6972     sel.cpMax--;\r
6973 \r
6974     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6975       MessageBeep(MB_ICONEXCLAMATION);\r
6976       return;\r
6977     }\r
6978     tr.chrg = sel;\r
6979     tr.lpstrText = name;\r
6980     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6981   } else {\r
6982     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6983       MessageBeep(MB_ICONEXCLAMATION);\r
6984       return;\r
6985     }\r
6986     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6987   }\r
6988   if (immediate) {\r
6989     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6990     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6991     SetWindowText(hInput, buf);\r
6992     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6993   } else {\r
6994     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6995       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6996     SetWindowText(hInput, buf);\r
6997     sel.cpMin = 999999;\r
6998     sel.cpMax = 999999;\r
6999     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7000     SetFocus(hInput);\r
7001   }\r
7002 }\r
7003 \r
7004 LRESULT CALLBACK \r
7005 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7006 {\r
7007   HWND hInput;\r
7008   CHARRANGE sel;\r
7009 \r
7010   switch (message) {\r
7011   case WM_KEYDOWN:\r
7012     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7013     if(wParam=='R') return 0;\r
7014     switch (wParam) {\r
7015     case VK_PRIOR:\r
7016       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7017       return 0;\r
7018     case VK_NEXT:\r
7019       sel.cpMin = 999999;\r
7020       sel.cpMax = 999999;\r
7021       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7022       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7023       return 0;\r
7024     }\r
7025     break;\r
7026   case WM_CHAR:\r
7027    if(wParam != '\022') {\r
7028     if (wParam == '\t') {\r
7029       if (GetKeyState(VK_SHIFT) < 0) {\r
7030         /* shifted */\r
7031         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7032         if (buttonDesc[0].hwnd) {\r
7033           SetFocus(buttonDesc[0].hwnd);\r
7034         } else {\r
7035           SetFocus(hwndMain);\r
7036         }\r
7037       } else {\r
7038         /* unshifted */\r
7039         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7040       }\r
7041     } else {\r
7042       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7043       JAWS_DELETE( SetFocus(hInput); )\r
7044       SendMessage(hInput, message, wParam, lParam);\r
7045     }\r
7046     return 0;\r
7047    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7048    lParam = -1;\r
7049   case WM_RBUTTONDOWN:\r
7050     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7051       /* Move selection here if it was empty */\r
7052       POINT pt;\r
7053       pt.x = LOWORD(lParam);\r
7054       pt.y = HIWORD(lParam);\r
7055       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7056       if (sel.cpMin == sel.cpMax) {\r
7057         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7058         sel.cpMax = sel.cpMin;\r
7059         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7060       }\r
7061       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7062 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7063       POINT pt;\r
7064       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7065       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7066       if (sel.cpMin == sel.cpMax) {\r
7067         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7068         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7069       }\r
7070       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7071         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7072       }\r
7073       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7074       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7075       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7076       MenuPopup(hwnd, pt, hmenu, -1);\r
7077 }\r
7078     }\r
7079     return 0;\r
7080   case WM_RBUTTONUP:\r
7081     if (GetKeyState(VK_SHIFT) & ~1) {\r
7082       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7083         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7084     }\r
7085     return 0;\r
7086   case WM_PASTE:\r
7087     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7088     SetFocus(hInput);\r
7089     return SendMessage(hInput, message, wParam, lParam);\r
7090   case WM_MBUTTONDOWN:\r
7091     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7092   case WM_COMMAND:\r
7093     switch (LOWORD(wParam)) {\r
7094     case IDM_QuickPaste:\r
7095       {\r
7096         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7097         if (sel.cpMin == sel.cpMax) {\r
7098           MessageBeep(MB_ICONEXCLAMATION);\r
7099           return 0;\r
7100         }\r
7101         SendMessage(hwnd, WM_COPY, 0, 0);\r
7102         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7103         SendMessage(hInput, WM_PASTE, 0, 0);\r
7104         SetFocus(hInput);\r
7105         return 0;\r
7106       }\r
7107     case IDM_Cut:\r
7108       SendMessage(hwnd, WM_CUT, 0, 0);\r
7109       return 0;\r
7110     case IDM_Paste:\r
7111       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7112       return 0;\r
7113     case IDM_Copy:\r
7114       SendMessage(hwnd, WM_COPY, 0, 0);\r
7115       return 0;\r
7116     default:\r
7117       {\r
7118         int i = LOWORD(wParam) - IDM_CommandX;\r
7119         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7120             icsTextMenuEntry[i].command != NULL) {\r
7121           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7122                    icsTextMenuEntry[i].getname,\r
7123                    icsTextMenuEntry[i].immediate);\r
7124           return 0;\r
7125         }\r
7126       }\r
7127       break;\r
7128     }\r
7129     break;\r
7130   }\r
7131   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7132 }\r
7133 \r
7134 WNDPROC consoleInputWindowProc;\r
7135 \r
7136 LRESULT CALLBACK\r
7137 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7138 {\r
7139   char buf[MSG_SIZ];\r
7140   char *p;\r
7141   static BOOL sendNextChar = FALSE;\r
7142   static BOOL quoteNextChar = FALSE;\r
7143   InputSource *is = consoleInputSource;\r
7144   CHARFORMAT cf;\r
7145   CHARRANGE sel;\r
7146 \r
7147   switch (message) {\r
7148   case WM_CHAR:\r
7149     if (!appData.localLineEditing || sendNextChar) {\r
7150       is->buf[0] = (CHAR) wParam;\r
7151       is->count = 1;\r
7152       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7153       sendNextChar = FALSE;\r
7154       return 0;\r
7155     }\r
7156     if (quoteNextChar) {\r
7157       buf[0] = (char) wParam;\r
7158       buf[1] = NULLCHAR;\r
7159       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7160       quoteNextChar = FALSE;\r
7161       return 0;\r
7162     }\r
7163     switch (wParam) {\r
7164     case '\r':   /* Enter key */\r
7165       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7166       if (consoleEcho) SaveInHistory(is->buf);\r
7167       is->buf[is->count++] = '\n';\r
7168       is->buf[is->count] = NULLCHAR;\r
7169       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7170       if (consoleEcho) {\r
7171         ConsoleOutput(is->buf, is->count, TRUE);\r
7172       } else if (appData.localLineEditing) {\r
7173         ConsoleOutput("\n", 1, TRUE);\r
7174       }\r
7175       /* fall thru */\r
7176     case '\033': /* Escape key */\r
7177       SetWindowText(hwnd, "");\r
7178       cf.cbSize = sizeof(CHARFORMAT);\r
7179       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7180       if (consoleEcho) {\r
7181         cf.crTextColor = textAttribs[ColorNormal].color;\r
7182       } else {\r
7183         cf.crTextColor = COLOR_ECHOOFF;\r
7184       }\r
7185       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7186       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7187       return 0;\r
7188     case '\t':   /* Tab key */\r
7189       if (GetKeyState(VK_SHIFT) < 0) {\r
7190         /* shifted */\r
7191         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7192       } else {\r
7193         /* unshifted */\r
7194         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7195         if (buttonDesc[0].hwnd) {\r
7196           SetFocus(buttonDesc[0].hwnd);\r
7197         } else {\r
7198           SetFocus(hwndMain);\r
7199         }\r
7200       }\r
7201       return 0;\r
7202     case '\023': /* Ctrl+S */\r
7203       sendNextChar = TRUE;\r
7204       return 0;\r
7205     case '\021': /* Ctrl+Q */\r
7206       quoteNextChar = TRUE;\r
7207       return 0;\r
7208     JAWS_REPLAY\r
7209     default:\r
7210       break;\r
7211     }\r
7212     break;\r
7213   case WM_KEYDOWN:\r
7214     switch (wParam) {\r
7215     case VK_UP:\r
7216       GetWindowText(hwnd, buf, MSG_SIZ);\r
7217       p = PrevInHistory(buf);\r
7218       if (p != NULL) {\r
7219         SetWindowText(hwnd, p);\r
7220         sel.cpMin = 999999;\r
7221         sel.cpMax = 999999;\r
7222         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7223         return 0;\r
7224       }\r
7225       break;\r
7226     case VK_DOWN:\r
7227       p = NextInHistory();\r
7228       if (p != NULL) {\r
7229         SetWindowText(hwnd, p);\r
7230         sel.cpMin = 999999;\r
7231         sel.cpMax = 999999;\r
7232         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7233         return 0;\r
7234       }\r
7235       break;\r
7236     case VK_HOME:\r
7237     case VK_END:\r
7238       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7239       /* fall thru */\r
7240     case VK_PRIOR:\r
7241     case VK_NEXT:\r
7242       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7243       return 0;\r
7244     }\r
7245     break;\r
7246   case WM_MBUTTONDOWN:\r
7247     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7248       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7249     break;\r
7250   case WM_RBUTTONUP:\r
7251     if (GetKeyState(VK_SHIFT) & ~1) {\r
7252       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7253         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7254     } else {\r
7255       POINT pt;\r
7256       HMENU hmenu;\r
7257       hmenu = LoadMenu(hInst, "InputMenu");\r
7258       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7259       if (sel.cpMin == sel.cpMax) {\r
7260         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7261         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7262       }\r
7263       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7264         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7265       }\r
7266       pt.x = LOWORD(lParam);\r
7267       pt.y = HIWORD(lParam);\r
7268       MenuPopup(hwnd, pt, hmenu, -1);\r
7269     }\r
7270     return 0;\r
7271   case WM_COMMAND:\r
7272     switch (LOWORD(wParam)) { \r
7273     case IDM_Undo:\r
7274       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7275       return 0;\r
7276     case IDM_SelectAll:\r
7277       sel.cpMin = 0;\r
7278       sel.cpMax = -1; /*999999?*/\r
7279       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7280       return 0;\r
7281     case IDM_Cut:\r
7282       SendMessage(hwnd, WM_CUT, 0, 0);\r
7283       return 0;\r
7284     case IDM_Paste:\r
7285       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7286       return 0;\r
7287     case IDM_Copy:\r
7288       SendMessage(hwnd, WM_COPY, 0, 0);\r
7289       return 0;\r
7290     }\r
7291     break;\r
7292   }\r
7293   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7294 }\r
7295 \r
7296 #define CO_MAX  100000\r
7297 #define CO_TRIM   1000\r
7298 \r
7299 LRESULT CALLBACK\r
7300 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7301 {\r
7302   static SnapData sd;\r
7303   HWND hText, hInput;\r
7304   RECT rect;\r
7305   static int sizeX, sizeY;\r
7306   int newSizeX, newSizeY;\r
7307   MINMAXINFO *mmi;\r
7308   WORD wMask;\r
7309 \r
7310   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7311   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7312 \r
7313   switch (message) {\r
7314   case WM_NOTIFY:\r
7315     if (((NMHDR*)lParam)->code == EN_LINK)\r
7316     {\r
7317       ENLINK *pLink = (ENLINK*)lParam;\r
7318       if (pLink->msg == WM_LBUTTONUP)\r
7319       {\r
7320         TEXTRANGE tr;\r
7321 \r
7322         tr.chrg = pLink->chrg;\r
7323         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7324         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7325         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7326         free(tr.lpstrText);\r
7327       }\r
7328     }\r
7329     break;\r
7330   case WM_INITDIALOG: /* message: initialize dialog box */\r
7331     hwndConsole = hDlg;\r
7332     SetFocus(hInput);\r
7333     consoleTextWindowProc = (WNDPROC)\r
7334       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7335     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7336     consoleInputWindowProc = (WNDPROC)\r
7337       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7338     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7339     Colorize(ColorNormal, TRUE);\r
7340     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7341     ChangedConsoleFont();\r
7342     GetClientRect(hDlg, &rect);\r
7343     sizeX = rect.right;\r
7344     sizeY = rect.bottom;\r
7345     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7346         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7347       WINDOWPLACEMENT wp;\r
7348       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7349       wp.length = sizeof(WINDOWPLACEMENT);\r
7350       wp.flags = 0;\r
7351       wp.showCmd = SW_SHOW;\r
7352       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7353       wp.rcNormalPosition.left = wpConsole.x;\r
7354       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7355       wp.rcNormalPosition.top = wpConsole.y;\r
7356       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7357       SetWindowPlacement(hDlg, &wp);\r
7358     }\r
7359 \r
7360    // [HGM] Chessknight's change 2004-07-13\r
7361    else { /* Determine Defaults */\r
7362        WINDOWPLACEMENT wp;\r
7363        wpConsole.x = wpMain.width + 1;\r
7364        wpConsole.y = wpMain.y;\r
7365        wpConsole.width = screenWidth -  wpMain.width;\r
7366        wpConsole.height = wpMain.height;\r
7367        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7368        wp.length = sizeof(WINDOWPLACEMENT);\r
7369        wp.flags = 0;\r
7370        wp.showCmd = SW_SHOW;\r
7371        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7372        wp.rcNormalPosition.left = wpConsole.x;\r
7373        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7374        wp.rcNormalPosition.top = wpConsole.y;\r
7375        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7376        SetWindowPlacement(hDlg, &wp);\r
7377     }\r
7378 \r
7379    // Allow hText to highlight URLs and send notifications on them\r
7380    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7381    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7382    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7383    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7384 \r
7385     return FALSE;\r
7386 \r
7387   case WM_SETFOCUS:\r
7388     SetFocus(hInput);\r
7389     return 0;\r
7390 \r
7391   case WM_CLOSE:\r
7392     ExitEvent(0);\r
7393     /* not reached */\r
7394     break;\r
7395 \r
7396   case WM_SIZE:\r
7397     if (IsIconic(hDlg)) break;\r
7398     newSizeX = LOWORD(lParam);\r
7399     newSizeY = HIWORD(lParam);\r
7400     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7401       RECT rectText, rectInput;\r
7402       POINT pt;\r
7403       int newTextHeight, newTextWidth;\r
7404       GetWindowRect(hText, &rectText);\r
7405       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7406       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7407       if (newTextHeight < 0) {\r
7408         newSizeY += -newTextHeight;\r
7409         newTextHeight = 0;\r
7410       }\r
7411       SetWindowPos(hText, NULL, 0, 0,\r
7412         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7413       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7414       pt.x = rectInput.left;\r
7415       pt.y = rectInput.top + newSizeY - sizeY;\r
7416       ScreenToClient(hDlg, &pt);\r
7417       SetWindowPos(hInput, NULL, \r
7418         pt.x, pt.y, /* needs client coords */   \r
7419         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7420         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7421     }\r
7422     sizeX = newSizeX;\r
7423     sizeY = newSizeY;\r
7424     break;\r
7425 \r
7426   case WM_GETMINMAXINFO:\r
7427     /* Prevent resizing window too small */\r
7428     mmi = (MINMAXINFO *) lParam;\r
7429     mmi->ptMinTrackSize.x = 100;\r
7430     mmi->ptMinTrackSize.y = 100;\r
7431     break;\r
7432 \r
7433   /* [AS] Snapping */\r
7434   case WM_ENTERSIZEMOVE:\r
7435     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7436 \r
7437   case WM_SIZING:\r
7438     return OnSizing( &sd, hDlg, wParam, lParam );\r
7439 \r
7440   case WM_MOVING:\r
7441     return OnMoving( &sd, hDlg, wParam, lParam );\r
7442 \r
7443   case WM_EXITSIZEMOVE:\r
7444         UpdateICSWidth(hText);\r
7445     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7446   }\r
7447 \r
7448   return DefWindowProc(hDlg, message, wParam, lParam);\r
7449 }\r
7450 \r
7451 \r
7452 VOID\r
7453 ConsoleCreate()\r
7454 {\r
7455   HWND hCons;\r
7456   if (hwndConsole) return;\r
7457   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7458   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7459 }\r
7460 \r
7461 \r
7462 VOID\r
7463 ConsoleOutput(char* data, int length, int forceVisible)\r
7464 {\r
7465   HWND hText;\r
7466   int trim, exlen;\r
7467   char *p, *q;\r
7468   char buf[CO_MAX+1];\r
7469   POINT pEnd;\r
7470   RECT rect;\r
7471   static int delayLF = 0;\r
7472   CHARRANGE savesel, sel;\r
7473 \r
7474   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7475   p = data;\r
7476   q = buf;\r
7477   if (delayLF) {\r
7478     *q++ = '\r';\r
7479     *q++ = '\n';\r
7480     delayLF = 0;\r
7481   }\r
7482   while (length--) {\r
7483     if (*p == '\n') {\r
7484       if (*++p) {\r
7485         *q++ = '\r';\r
7486         *q++ = '\n';\r
7487       } else {\r
7488         delayLF = 1;\r
7489       }\r
7490     } else if (*p == '\007') {\r
7491        MyPlaySound(&sounds[(int)SoundBell]);\r
7492        p++;\r
7493     } else {\r
7494       *q++ = *p++;\r
7495     }\r
7496   }\r
7497   *q = NULLCHAR;\r
7498   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7499   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7500   /* Save current selection */\r
7501   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7502   exlen = GetWindowTextLength(hText);\r
7503   /* Find out whether current end of text is visible */\r
7504   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7505   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7506   /* Trim existing text if it's too long */\r
7507   if (exlen + (q - buf) > CO_MAX) {\r
7508     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7509     sel.cpMin = 0;\r
7510     sel.cpMax = trim;\r
7511     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7512     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7513     exlen -= trim;\r
7514     savesel.cpMin -= trim;\r
7515     savesel.cpMax -= trim;\r
7516     if (exlen < 0) exlen = 0;\r
7517     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7518     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7519   }\r
7520   /* Append the new text */\r
7521   sel.cpMin = exlen;\r
7522   sel.cpMax = exlen;\r
7523   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7524   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7525   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7526   if (forceVisible || exlen == 0 ||\r
7527       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7528        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7529     /* Scroll to make new end of text visible if old end of text\r
7530        was visible or new text is an echo of user typein */\r
7531     sel.cpMin = 9999999;\r
7532     sel.cpMax = 9999999;\r
7533     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7534     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7535     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7536     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7537   }\r
7538   if (savesel.cpMax == exlen || forceVisible) {\r
7539     /* Move insert point to new end of text if it was at the old\r
7540        end of text or if the new text is an echo of user typein */\r
7541     sel.cpMin = 9999999;\r
7542     sel.cpMax = 9999999;\r
7543     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7544   } else {\r
7545     /* Restore previous selection */\r
7546     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7547   }\r
7548   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7549 }\r
7550 \r
7551 /*---------*/\r
7552 \r
7553 \r
7554 void\r
7555 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7556 {\r
7557   char buf[100];\r
7558   char *str;\r
7559   COLORREF oldFg, oldBg;\r
7560   HFONT oldFont;\r
7561   RECT rect;\r
7562 \r
7563   if(copyNumber > 1)\r
7564     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7565 \r
7566   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7567   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7568   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7569 \r
7570   rect.left = x;\r
7571   rect.right = x + squareSize;\r
7572   rect.top  = y;\r
7573   rect.bottom = y + squareSize;\r
7574   str = buf;\r
7575 \r
7576   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7577                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7578              y, ETO_CLIPPED|ETO_OPAQUE,\r
7579              &rect, str, strlen(str), NULL);\r
7580 \r
7581   (void) SetTextColor(hdc, oldFg);\r
7582   (void) SetBkColor(hdc, oldBg);\r
7583   (void) SelectObject(hdc, oldFont);\r
7584 }\r
7585 \r
7586 void\r
7587 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7588               RECT *rect, char *color, char *flagFell)\r
7589 {\r
7590   char buf[100];\r
7591   char *str;\r
7592   COLORREF oldFg, oldBg;\r
7593   HFONT oldFont;\r
7594 \r
7595   if (twoBoards && partnerUp) return;\r
7596   if (appData.clockMode) {\r
7597     if (tinyLayout)\r
7598       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7599     else\r
7600       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7601     str = buf;\r
7602   } else {\r
7603     str = color;\r
7604   }\r
7605 \r
7606   if (highlight) {\r
7607     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7608     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7609   } else {\r
7610     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7611     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7612   }\r
7613   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7614 \r
7615   JAWS_SILENCE\r
7616 \r
7617   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7618              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7619              rect, str, strlen(str), NULL);\r
7620   if(logoHeight > 0 && appData.clockMode) {\r
7621       RECT r;\r
7622       str += strlen(color)+2;\r
7623       r.top = rect->top + logoHeight/2;\r
7624       r.left = rect->left;\r
7625       r.right = rect->right;\r
7626       r.bottom = rect->bottom;\r
7627       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7628                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7629                  &r, str, strlen(str), NULL);\r
7630   }\r
7631   (void) SetTextColor(hdc, oldFg);\r
7632   (void) SetBkColor(hdc, oldBg);\r
7633   (void) SelectObject(hdc, oldFont);\r
7634 }\r
7635 \r
7636 \r
7637 int\r
7638 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7639            OVERLAPPED *ovl)\r
7640 {\r
7641   int ok, err;\r
7642 \r
7643   /* [AS]  */\r
7644   if( count <= 0 ) {\r
7645     if (appData.debugMode) {\r
7646       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7647     }\r
7648 \r
7649     return ERROR_INVALID_USER_BUFFER;\r
7650   }\r
7651 \r
7652   ResetEvent(ovl->hEvent);\r
7653   ovl->Offset = ovl->OffsetHigh = 0;\r
7654   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7655   if (ok) {\r
7656     err = NO_ERROR;\r
7657   } else {\r
7658     err = GetLastError();\r
7659     if (err == ERROR_IO_PENDING) {\r
7660       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7661       if (ok)\r
7662         err = NO_ERROR;\r
7663       else\r
7664         err = GetLastError();\r
7665     }\r
7666   }\r
7667   return err;\r
7668 }\r
7669 \r
7670 int\r
7671 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7672             OVERLAPPED *ovl)\r
7673 {\r
7674   int ok, err;\r
7675 \r
7676   ResetEvent(ovl->hEvent);\r
7677   ovl->Offset = ovl->OffsetHigh = 0;\r
7678   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7679   if (ok) {\r
7680     err = NO_ERROR;\r
7681   } else {\r
7682     err = GetLastError();\r
7683     if (err == ERROR_IO_PENDING) {\r
7684       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7685       if (ok)\r
7686         err = NO_ERROR;\r
7687       else\r
7688         err = GetLastError();\r
7689     }\r
7690   }\r
7691   return err;\r
7692 }\r
7693 \r
7694 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7695 void CheckForInputBufferFull( InputSource * is )\r
7696 {\r
7697     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7698         /* Look for end of line */\r
7699         char * p = is->buf;\r
7700         \r
7701         while( p < is->next && *p != '\n' ) {\r
7702             p++;\r
7703         }\r
7704 \r
7705         if( p >= is->next ) {\r
7706             if (appData.debugMode) {\r
7707                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7708             }\r
7709 \r
7710             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7711             is->count = (DWORD) -1;\r
7712             is->next = is->buf;\r
7713         }\r
7714     }\r
7715 }\r
7716 \r
7717 DWORD\r
7718 InputThread(LPVOID arg)\r
7719 {\r
7720   InputSource *is;\r
7721   OVERLAPPED ovl;\r
7722 \r
7723   is = (InputSource *) arg;\r
7724   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7725   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7726   while (is->hThread != NULL) {\r
7727     is->error = DoReadFile(is->hFile, is->next,\r
7728                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7729                            &is->count, &ovl);\r
7730     if (is->error == NO_ERROR) {\r
7731       is->next += is->count;\r
7732     } else {\r
7733       if (is->error == ERROR_BROKEN_PIPE) {\r
7734         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7735         is->count = 0;\r
7736       } else {\r
7737         is->count = (DWORD) -1;\r
7738         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7739         break; \r
7740       }\r
7741     }\r
7742 \r
7743     CheckForInputBufferFull( is );\r
7744 \r
7745     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7746 \r
7747     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7748 \r
7749     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7750   }\r
7751 \r
7752   CloseHandle(ovl.hEvent);\r
7753   CloseHandle(is->hFile);\r
7754 \r
7755   if (appData.debugMode) {\r
7756     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7757   }\r
7758 \r
7759   return 0;\r
7760 }\r
7761 \r
7762 \r
7763 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7764 DWORD\r
7765 NonOvlInputThread(LPVOID arg)\r
7766 {\r
7767   InputSource *is;\r
7768   char *p, *q;\r
7769   int i;\r
7770   char prev;\r
7771 \r
7772   is = (InputSource *) arg;\r
7773   while (is->hThread != NULL) {\r
7774     is->error = ReadFile(is->hFile, is->next,\r
7775                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7776                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7777     if (is->error == NO_ERROR) {\r
7778       /* Change CRLF to LF */\r
7779       if (is->next > is->buf) {\r
7780         p = is->next - 1;\r
7781         i = is->count + 1;\r
7782       } else {\r
7783         p = is->next;\r
7784         i = is->count;\r
7785       }\r
7786       q = p;\r
7787       prev = NULLCHAR;\r
7788       while (i > 0) {\r
7789         if (prev == '\r' && *p == '\n') {\r
7790           *(q-1) = '\n';\r
7791           is->count--;\r
7792         } else { \r
7793           *q++ = *p;\r
7794         }\r
7795         prev = *p++;\r
7796         i--;\r
7797       }\r
7798       *q = NULLCHAR;\r
7799       is->next = q;\r
7800     } else {\r
7801       if (is->error == ERROR_BROKEN_PIPE) {\r
7802         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7803         is->count = 0; \r
7804       } else {\r
7805         is->count = (DWORD) -1;\r
7806       }\r
7807     }\r
7808 \r
7809     CheckForInputBufferFull( is );\r
7810 \r
7811     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7812 \r
7813     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7814 \r
7815     if (is->count < 0) break;  /* Quit on error */\r
7816   }\r
7817   CloseHandle(is->hFile);\r
7818   return 0;\r
7819 }\r
7820 \r
7821 DWORD\r
7822 SocketInputThread(LPVOID arg)\r
7823 {\r
7824   InputSource *is;\r
7825 \r
7826   is = (InputSource *) arg;\r
7827   while (is->hThread != NULL) {\r
7828     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7829     if ((int)is->count == SOCKET_ERROR) {\r
7830       is->count = (DWORD) -1;\r
7831       is->error = WSAGetLastError();\r
7832     } else {\r
7833       is->error = NO_ERROR;\r
7834       is->next += is->count;\r
7835       if (is->count == 0 && is->second == is) {\r
7836         /* End of file on stderr; quit with no message */\r
7837         break;\r
7838       }\r
7839     }\r
7840     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7841 \r
7842     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7843 \r
7844     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7845   }\r
7846   return 0;\r
7847 }\r
7848 \r
7849 VOID\r
7850 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7851 {\r
7852   InputSource *is;\r
7853 \r
7854   is = (InputSource *) lParam;\r
7855   if (is->lineByLine) {\r
7856     /* Feed in lines one by one */\r
7857     char *p = is->buf;\r
7858     char *q = p;\r
7859     while (q < is->next) {\r
7860       if (*q++ == '\n') {\r
7861         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7862         p = q;\r
7863       }\r
7864     }\r
7865     \r
7866     /* Move any partial line to the start of the buffer */\r
7867     q = is->buf;\r
7868     while (p < is->next) {\r
7869       *q++ = *p++;\r
7870     }\r
7871     is->next = q;\r
7872 \r
7873     if (is->error != NO_ERROR || is->count == 0) {\r
7874       /* Notify backend of the error.  Note: If there was a partial\r
7875          line at the end, it is not flushed through. */\r
7876       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7877     }\r
7878   } else {\r
7879     /* Feed in the whole chunk of input at once */\r
7880     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7881     is->next = is->buf;\r
7882   }\r
7883 }\r
7884 \r
7885 /*---------------------------------------------------------------------------*\\r
7886  *\r
7887  *  Menu enables. Used when setting various modes.\r
7888  *\r
7889 \*---------------------------------------------------------------------------*/\r
7890 \r
7891 typedef struct {\r
7892   int item;\r
7893   int flags;\r
7894 } Enables;\r
7895 \r
7896 VOID\r
7897 GreyRevert(Boolean grey)\r
7898 { // [HGM] vari: for retracting variations in local mode\r
7899   HMENU hmenu = GetMenu(hwndMain);\r
7900   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7901   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7902 }\r
7903 \r
7904 VOID\r
7905 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7906 {\r
7907   while (enab->item > 0) {\r
7908     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7909     enab++;\r
7910   }\r
7911 }\r
7912 \r
7913 Enables gnuEnables[] = {\r
7914   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7927 \r
7928   // Needed to switch from ncp to GNU mode on Engine Load\r
7929   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7930   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7940   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7941   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7942   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7943   { -1, -1 }\r
7944 };\r
7945 \r
7946 Enables icsEnables[] = {\r
7947   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7963   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7967   { -1, -1 }\r
7968 };\r
7969 \r
7970 #if ZIPPY\r
7971 Enables zippyEnables[] = {\r
7972   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7976   { -1, -1 }\r
7977 };\r
7978 #endif\r
7979 \r
7980 Enables ncpEnables[] = {\r
7981   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7987   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7988   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7989   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7990   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7991   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8003   { -1, -1 }\r
8004 };\r
8005 \r
8006 Enables trainingOnEnables[] = {\r
8007   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8016   { -1, -1 }\r
8017 };\r
8018 \r
8019 Enables trainingOffEnables[] = {\r
8020   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8021   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8029   { -1, -1 }\r
8030 };\r
8031 \r
8032 /* These modify either ncpEnables or gnuEnables */\r
8033 Enables cmailEnables[] = {\r
8034   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8036   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8037   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8041   { -1, -1 }\r
8042 };\r
8043 \r
8044 Enables machineThinkingEnables[] = {\r
8045   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8055   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8058 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8061   { -1, -1 }\r
8062 };\r
8063 \r
8064 Enables userThinkingEnables[] = {\r
8065   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8066   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8067   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8068   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8069   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8070   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8072   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8078 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8081   { -1, -1 }\r
8082 };\r
8083 \r
8084 /*---------------------------------------------------------------------------*\\r
8085  *\r
8086  *  Front-end interface functions exported by XBoard.\r
8087  *  Functions appear in same order as prototypes in frontend.h.\r
8088  * \r
8089 \*---------------------------------------------------------------------------*/\r
8090 VOID\r
8091 CheckMark(UINT item, int state)\r
8092 {\r
8093     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8094 }\r
8095 \r
8096 VOID\r
8097 ModeHighlight()\r
8098 {\r
8099   static UINT prevChecked = 0;\r
8100   static int prevPausing = 0;\r
8101   UINT nowChecked;\r
8102 \r
8103   if (pausing != prevPausing) {\r
8104     prevPausing = pausing;\r
8105     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8106                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8107     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8108   }\r
8109 \r
8110   switch (gameMode) {\r
8111   case BeginningOfGame:\r
8112     if (appData.icsActive)\r
8113       nowChecked = IDM_IcsClient;\r
8114     else if (appData.noChessProgram)\r
8115       nowChecked = IDM_EditGame;\r
8116     else\r
8117       nowChecked = IDM_MachineBlack;\r
8118     break;\r
8119   case MachinePlaysBlack:\r
8120     nowChecked = IDM_MachineBlack;\r
8121     break;\r
8122   case MachinePlaysWhite:\r
8123     nowChecked = IDM_MachineWhite;\r
8124     break;\r
8125   case TwoMachinesPlay:\r
8126     nowChecked = IDM_TwoMachines;\r
8127     break;\r
8128   case AnalyzeMode:\r
8129     nowChecked = IDM_AnalysisMode;\r
8130     break;\r
8131   case AnalyzeFile:\r
8132     nowChecked = IDM_AnalyzeFile;\r
8133     break;\r
8134   case EditGame:\r
8135     nowChecked = IDM_EditGame;\r
8136     break;\r
8137   case PlayFromGameFile:\r
8138     nowChecked = IDM_LoadGame;\r
8139     break;\r
8140   case EditPosition:\r
8141     nowChecked = IDM_EditPosition;\r
8142     break;\r
8143   case Training:\r
8144     nowChecked = IDM_Training;\r
8145     break;\r
8146   case IcsPlayingWhite:\r
8147   case IcsPlayingBlack:\r
8148   case IcsObserving:\r
8149   case IcsIdle:\r
8150     nowChecked = IDM_IcsClient;\r
8151     break;\r
8152   default:\r
8153   case EndOfGame:\r
8154     nowChecked = 0;\r
8155     break;\r
8156   }\r
8157   CheckMark(prevChecked, MF_UNCHECKED);\r
8158   CheckMark(nowChecked, MF_CHECKED);\r
8159   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8160 \r
8161   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8162     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8163                           MF_BYCOMMAND|MF_ENABLED);\r
8164   } else {\r
8165     (void) EnableMenuItem(GetMenu(hwndMain), \r
8166                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8167   }\r
8168 \r
8169   prevChecked = nowChecked;\r
8170 \r
8171   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8172   if (appData.icsActive) {\r
8173        if (appData.icsEngineAnalyze) {\r
8174                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8175        } else {\r
8176                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8177        }\r
8178   }\r
8179   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8180 }\r
8181 \r
8182 VOID\r
8183 SetICSMode()\r
8184 {\r
8185   HMENU hmenu = GetMenu(hwndMain);\r
8186   SetMenuEnables(hmenu, icsEnables);\r
8187   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8188     MF_BYCOMMAND|MF_ENABLED);\r
8189 #if ZIPPY\r
8190   if (appData.zippyPlay) {\r
8191     SetMenuEnables(hmenu, zippyEnables);\r
8192     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8193          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8194           MF_BYCOMMAND|MF_ENABLED);\r
8195   }\r
8196 #endif\r
8197 }\r
8198 \r
8199 VOID\r
8200 SetGNUMode()\r
8201 {\r
8202   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8203 }\r
8204 \r
8205 VOID\r
8206 SetNCPMode()\r
8207 {\r
8208   HMENU hmenu = GetMenu(hwndMain);\r
8209   SetMenuEnables(hmenu, ncpEnables);\r
8210     DrawMenuBar(hwndMain);\r
8211 }\r
8212 \r
8213 VOID\r
8214 SetCmailMode()\r
8215 {\r
8216   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8217 }\r
8218 \r
8219 VOID \r
8220 SetTrainingModeOn()\r
8221 {\r
8222   int i;\r
8223   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8224   for (i = 0; i < N_BUTTONS; i++) {\r
8225     if (buttonDesc[i].hwnd != NULL)\r
8226       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8227   }\r
8228   CommentPopDown();\r
8229 }\r
8230 \r
8231 VOID SetTrainingModeOff()\r
8232 {\r
8233   int i;\r
8234   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8235   for (i = 0; i < N_BUTTONS; i++) {\r
8236     if (buttonDesc[i].hwnd != NULL)\r
8237       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8238   }\r
8239 }\r
8240 \r
8241 \r
8242 VOID\r
8243 SetUserThinkingEnables()\r
8244 {\r
8245   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8246 }\r
8247 \r
8248 VOID\r
8249 SetMachineThinkingEnables()\r
8250 {\r
8251   HMENU hMenu = GetMenu(hwndMain);\r
8252   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8253 \r
8254   SetMenuEnables(hMenu, machineThinkingEnables);\r
8255 \r
8256   if (gameMode == MachinePlaysBlack) {\r
8257     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8258   } else if (gameMode == MachinePlaysWhite) {\r
8259     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8260   } else if (gameMode == TwoMachinesPlay) {\r
8261     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8262   }\r
8263 }\r
8264 \r
8265 \r
8266 VOID\r
8267 DisplayTitle(char *str)\r
8268 {\r
8269   char title[MSG_SIZ], *host;\r
8270   if (str[0] != NULLCHAR) {\r
8271     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8272   } else if (appData.icsActive) {\r
8273     if (appData.icsCommPort[0] != NULLCHAR)\r
8274       host = "ICS";\r
8275     else \r
8276       host = appData.icsHost;\r
8277       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8278   } else if (appData.noChessProgram) {\r
8279     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8280   } else {\r
8281     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8282     strcat(title, ": ");\r
8283     strcat(title, first.tidy);\r
8284   }\r
8285   SetWindowText(hwndMain, title);\r
8286 }\r
8287 \r
8288 \r
8289 VOID\r
8290 DisplayMessage(char *str1, char *str2)\r
8291 {\r
8292   HDC hdc;\r
8293   HFONT oldFont;\r
8294   int remain = MESSAGE_TEXT_MAX - 1;\r
8295   int len;\r
8296 \r
8297   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8298   messageText[0] = NULLCHAR;\r
8299   if (*str1) {\r
8300     len = strlen(str1);\r
8301     if (len > remain) len = remain;\r
8302     strncpy(messageText, str1, len);\r
8303     messageText[len] = NULLCHAR;\r
8304     remain -= len;\r
8305   }\r
8306   if (*str2 && remain >= 2) {\r
8307     if (*str1) {\r
8308       strcat(messageText, "  ");\r
8309       remain -= 2;\r
8310     }\r
8311     len = strlen(str2);\r
8312     if (len > remain) len = remain;\r
8313     strncat(messageText, str2, len);\r
8314   }\r
8315   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8316   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8317 \r
8318   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8319 \r
8320   SAYMACHINEMOVE();\r
8321 \r
8322   hdc = GetDC(hwndMain);\r
8323   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8324   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8325              &messageRect, messageText, strlen(messageText), NULL);\r
8326   (void) SelectObject(hdc, oldFont);\r
8327   (void) ReleaseDC(hwndMain, hdc);\r
8328 }\r
8329 \r
8330 VOID\r
8331 DisplayError(char *str, int error)\r
8332 {\r
8333   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8334   int len;\r
8335 \r
8336   if (error == 0) {\r
8337     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8338   } else {\r
8339     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8340                         NULL, error, LANG_NEUTRAL,\r
8341                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8342     if (len > 0) {\r
8343       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8344     } else {\r
8345       ErrorMap *em = errmap;\r
8346       while (em->err != 0 && em->err != error) em++;\r
8347       if (em->err != 0) {\r
8348         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8349       } else {\r
8350         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8351       }\r
8352     }\r
8353   }\r
8354   \r
8355   ErrorPopUp(_("Error"), buf);\r
8356 }\r
8357 \r
8358 \r
8359 VOID\r
8360 DisplayMoveError(char *str)\r
8361 {\r
8362   fromX = fromY = -1;\r
8363   ClearHighlights();\r
8364   DrawPosition(FALSE, NULL);\r
8365   if (appData.popupMoveErrors) {\r
8366     ErrorPopUp(_("Error"), str);\r
8367   } else {\r
8368     DisplayMessage(str, "");\r
8369     moveErrorMessageUp = TRUE;\r
8370   }\r
8371 }\r
8372 \r
8373 VOID\r
8374 DisplayFatalError(char *str, int error, int exitStatus)\r
8375 {\r
8376   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8377   int len;\r
8378   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8379 \r
8380   if (error != 0) {\r
8381     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8382                         NULL, error, LANG_NEUTRAL,\r
8383                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8384     if (len > 0) {\r
8385       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8386     } else {\r
8387       ErrorMap *em = errmap;\r
8388       while (em->err != 0 && em->err != error) em++;\r
8389       if (em->err != 0) {\r
8390         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8391       } else {\r
8392         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8393       }\r
8394     }\r
8395     str = buf;\r
8396   }\r
8397   if (appData.debugMode) {\r
8398     fprintf(debugFP, "%s: %s\n", label, str);\r
8399   }\r
8400   if (appData.popupExitMessage) {\r
8401     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8402                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8403   }\r
8404   ExitEvent(exitStatus);\r
8405 }\r
8406 \r
8407 \r
8408 VOID\r
8409 DisplayInformation(char *str)\r
8410 {\r
8411   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8412 }\r
8413 \r
8414 \r
8415 VOID\r
8416 DisplayNote(char *str)\r
8417 {\r
8418   ErrorPopUp(_("Note"), str);\r
8419 }\r
8420 \r
8421 \r
8422 typedef struct {\r
8423   char *title, *question, *replyPrefix;\r
8424   ProcRef pr;\r
8425 } QuestionParams;\r
8426 \r
8427 LRESULT CALLBACK\r
8428 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8429 {\r
8430   static QuestionParams *qp;\r
8431   char reply[MSG_SIZ];\r
8432   int len, err;\r
8433 \r
8434   switch (message) {\r
8435   case WM_INITDIALOG:\r
8436     qp = (QuestionParams *) lParam;\r
8437     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8438     Translate(hDlg, DLG_Question);\r
8439     SetWindowText(hDlg, qp->title);\r
8440     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8441     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8442     return FALSE;\r
8443 \r
8444   case WM_COMMAND:\r
8445     switch (LOWORD(wParam)) {\r
8446     case IDOK:\r
8447       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8448       if (*reply) strcat(reply, " ");\r
8449       len = strlen(reply);\r
8450       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8451       strcat(reply, "\n");\r
8452       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8453       EndDialog(hDlg, TRUE);\r
8454       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8455       return TRUE;\r
8456     case IDCANCEL:\r
8457       EndDialog(hDlg, FALSE);\r
8458       return TRUE;\r
8459     default:\r
8460       break;\r
8461     }\r
8462     break;\r
8463   }\r
8464   return FALSE;\r
8465 }\r
8466 \r
8467 VOID\r
8468 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8469 {\r
8470     QuestionParams qp;\r
8471     FARPROC lpProc;\r
8472     \r
8473     qp.title = title;\r
8474     qp.question = question;\r
8475     qp.replyPrefix = replyPrefix;\r
8476     qp.pr = pr;\r
8477     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8478     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8479       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8480     FreeProcInstance(lpProc);\r
8481 }\r
8482 \r
8483 /* [AS] Pick FRC position */\r
8484 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8485 {\r
8486     static int * lpIndexFRC;\r
8487     BOOL index_is_ok;\r
8488     char buf[16];\r
8489 \r
8490     switch( message )\r
8491     {\r
8492     case WM_INITDIALOG:\r
8493         lpIndexFRC = (int *) lParam;\r
8494 \r
8495         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8496         Translate(hDlg, DLG_NewGameFRC);\r
8497 \r
8498         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8499         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8500         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8501         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8502 \r
8503         break;\r
8504 \r
8505     case WM_COMMAND:\r
8506         switch( LOWORD(wParam) ) {\r
8507         case IDOK:\r
8508             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8509             EndDialog( hDlg, 0 );\r
8510             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8511             return TRUE;\r
8512         case IDCANCEL:\r
8513             EndDialog( hDlg, 1 );   \r
8514             return TRUE;\r
8515         case IDC_NFG_Edit:\r
8516             if( HIWORD(wParam) == EN_CHANGE ) {\r
8517                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8518 \r
8519                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8520             }\r
8521             return TRUE;\r
8522         case IDC_NFG_Random:\r
8523           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8524             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8525             return TRUE;\r
8526         }\r
8527 \r
8528         break;\r
8529     }\r
8530 \r
8531     return FALSE;\r
8532 }\r
8533 \r
8534 int NewGameFRC()\r
8535 {\r
8536     int result;\r
8537     int index = appData.defaultFrcPosition;\r
8538     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8539 \r
8540     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8541 \r
8542     if( result == 0 ) {\r
8543         appData.defaultFrcPosition = index;\r
8544     }\r
8545 \r
8546     return result;\r
8547 }\r
8548 \r
8549 /* [AS] Game list options. Refactored by HGM */\r
8550 \r
8551 HWND gameListOptionsDialog;\r
8552 \r
8553 // low-level front-end: clear text edit / list widget\r
8554 void\r
8555 GLT_ClearList()\r
8556 {\r
8557     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8558 }\r
8559 \r
8560 // low-level front-end: clear text edit / list widget\r
8561 void\r
8562 GLT_DeSelectList()\r
8563 {\r
8564     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8565 }\r
8566 \r
8567 // low-level front-end: append line to text edit / list widget\r
8568 void\r
8569 GLT_AddToList( char *name )\r
8570 {\r
8571     if( name != 0 ) {\r
8572             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8573     }\r
8574 }\r
8575 \r
8576 // low-level front-end: get line from text edit / list widget\r
8577 Boolean\r
8578 GLT_GetFromList( int index, char *name )\r
8579 {\r
8580     if( name != 0 ) {\r
8581             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8582                 return TRUE;\r
8583     }\r
8584     return FALSE;\r
8585 }\r
8586 \r
8587 void GLT_MoveSelection( HWND hDlg, int delta )\r
8588 {\r
8589     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8590     int idx2 = idx1 + delta;\r
8591     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8592 \r
8593     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8594         char buf[128];\r
8595 \r
8596         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8597         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8598         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8599         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8600     }\r
8601 }\r
8602 \r
8603 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8604 {\r
8605     switch( message )\r
8606     {\r
8607     case WM_INITDIALOG:\r
8608         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8609         \r
8610         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8611         Translate(hDlg, DLG_GameListOptions);\r
8612 \r
8613         /* Initialize list */\r
8614         GLT_TagsToList( lpUserGLT );\r
8615 \r
8616         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8617 \r
8618         break;\r
8619 \r
8620     case WM_COMMAND:\r
8621         switch( LOWORD(wParam) ) {\r
8622         case IDOK:\r
8623             GLT_ParseList();\r
8624             EndDialog( hDlg, 0 );\r
8625             return TRUE;\r
8626         case IDCANCEL:\r
8627             EndDialog( hDlg, 1 );\r
8628             return TRUE;\r
8629 \r
8630         case IDC_GLT_Default:\r
8631             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8632             return TRUE;\r
8633 \r
8634         case IDC_GLT_Restore:\r
8635             GLT_TagsToList( appData.gameListTags );\r
8636             return TRUE;\r
8637 \r
8638         case IDC_GLT_Up:\r
8639             GLT_MoveSelection( hDlg, -1 );\r
8640             return TRUE;\r
8641 \r
8642         case IDC_GLT_Down:\r
8643             GLT_MoveSelection( hDlg, +1 );\r
8644             return TRUE;\r
8645         }\r
8646 \r
8647         break;\r
8648     }\r
8649 \r
8650     return FALSE;\r
8651 }\r
8652 \r
8653 int GameListOptions()\r
8654 {\r
8655     int result;\r
8656     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8657 \r
8658       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8659 \r
8660     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8661 \r
8662     if( result == 0 ) {\r
8663         /* [AS] Memory leak here! */\r
8664         appData.gameListTags = strdup( lpUserGLT ); \r
8665     }\r
8666 \r
8667     return result;\r
8668 }\r
8669 \r
8670 VOID\r
8671 DisplayIcsInteractionTitle(char *str)\r
8672 {\r
8673   char consoleTitle[MSG_SIZ];\r
8674 \r
8675     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8676     SetWindowText(hwndConsole, consoleTitle);\r
8677 \r
8678     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8679       char buf[MSG_SIZ], *p = buf, *q;\r
8680         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8681       do {\r
8682         q = strchr(p, ';');\r
8683         if(q) *q++ = 0;\r
8684         if(*p) ChatPopUp(p);\r
8685       } while(p=q);\r
8686     }\r
8687 \r
8688     SetActiveWindow(hwndMain);\r
8689 }\r
8690 \r
8691 void\r
8692 DrawPosition(int fullRedraw, Board board)\r
8693 {\r
8694   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8695 }\r
8696 \r
8697 void NotifyFrontendLogin()\r
8698 {\r
8699         if (hwndConsole)\r
8700                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8701 }\r
8702 \r
8703 VOID\r
8704 ResetFrontEnd()\r
8705 {\r
8706   fromX = fromY = -1;\r
8707   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8708     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8709     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8710     dragInfo.lastpos = dragInfo.pos;\r
8711     dragInfo.start.x = dragInfo.start.y = -1;\r
8712     dragInfo.from = dragInfo.start;\r
8713     ReleaseCapture();\r
8714     DrawPosition(TRUE, NULL);\r
8715   }\r
8716   TagsPopDown();\r
8717 }\r
8718 \r
8719 \r
8720 VOID\r
8721 CommentPopUp(char *title, char *str)\r
8722 {\r
8723   HWND hwnd = GetActiveWindow();\r
8724   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8725   SAY(str);\r
8726   SetActiveWindow(hwnd);\r
8727 }\r
8728 \r
8729 VOID\r
8730 CommentPopDown(void)\r
8731 {\r
8732   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8733   if (commentDialog) {\r
8734     ShowWindow(commentDialog, SW_HIDE);\r
8735   }\r
8736   commentUp = FALSE;\r
8737 }\r
8738 \r
8739 VOID\r
8740 EditCommentPopUp(int index, char *title, char *str)\r
8741 {\r
8742   EitherCommentPopUp(index, title, str, TRUE);\r
8743 }\r
8744 \r
8745 \r
8746 VOID\r
8747 RingBell()\r
8748 {\r
8749   MyPlaySound(&sounds[(int)SoundMove]);\r
8750 }\r
8751 \r
8752 VOID PlayIcsWinSound()\r
8753 {\r
8754   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8755 }\r
8756 \r
8757 VOID PlayIcsLossSound()\r
8758 {\r
8759   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8760 }\r
8761 \r
8762 VOID PlayIcsDrawSound()\r
8763 {\r
8764   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8765 }\r
8766 \r
8767 VOID PlayIcsUnfinishedSound()\r
8768 {\r
8769   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8770 }\r
8771 \r
8772 VOID\r
8773 PlayAlarmSound()\r
8774 {\r
8775   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8776 }\r
8777 \r
8778 VOID\r
8779 PlayTellSound()\r
8780 {\r
8781   MyPlaySound(&textAttribs[ColorTell].sound);\r
8782 }\r
8783 \r
8784 \r
8785 VOID\r
8786 EchoOn()\r
8787 {\r
8788   HWND hInput;\r
8789   consoleEcho = TRUE;\r
8790   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8791   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8792   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8793 }\r
8794 \r
8795 \r
8796 VOID\r
8797 EchoOff()\r
8798 {\r
8799   CHARFORMAT cf;\r
8800   HWND hInput;\r
8801   consoleEcho = FALSE;\r
8802   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8803   /* This works OK: set text and background both to the same color */\r
8804   cf = consoleCF;\r
8805   cf.crTextColor = COLOR_ECHOOFF;\r
8806   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8807   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8808 }\r
8809 \r
8810 /* No Raw()...? */\r
8811 \r
8812 void Colorize(ColorClass cc, int continuation)\r
8813 {\r
8814   currentColorClass = cc;\r
8815   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8816   consoleCF.crTextColor = textAttribs[cc].color;\r
8817   consoleCF.dwEffects = textAttribs[cc].effects;\r
8818   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8819 }\r
8820 \r
8821 char *\r
8822 UserName()\r
8823 {\r
8824   static char buf[MSG_SIZ];\r
8825   DWORD bufsiz = MSG_SIZ;\r
8826 \r
8827   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8828         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8829   }\r
8830   if (!GetUserName(buf, &bufsiz)) {\r
8831     /*DisplayError("Error getting user name", GetLastError());*/\r
8832     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8833   }\r
8834   return buf;\r
8835 }\r
8836 \r
8837 char *\r
8838 HostName()\r
8839 {\r
8840   static char buf[MSG_SIZ];\r
8841   DWORD bufsiz = MSG_SIZ;\r
8842 \r
8843   if (!GetComputerName(buf, &bufsiz)) {\r
8844     /*DisplayError("Error getting host name", GetLastError());*/\r
8845     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8846   }\r
8847   return buf;\r
8848 }\r
8849 \r
8850 \r
8851 int\r
8852 ClockTimerRunning()\r
8853 {\r
8854   return clockTimerEvent != 0;\r
8855 }\r
8856 \r
8857 int\r
8858 StopClockTimer()\r
8859 {\r
8860   if (clockTimerEvent == 0) return FALSE;\r
8861   KillTimer(hwndMain, clockTimerEvent);\r
8862   clockTimerEvent = 0;\r
8863   return TRUE;\r
8864 }\r
8865 \r
8866 void\r
8867 StartClockTimer(long millisec)\r
8868 {\r
8869   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8870                              (UINT) millisec, NULL);\r
8871 }\r
8872 \r
8873 void\r
8874 DisplayWhiteClock(long timeRemaining, int highlight)\r
8875 {\r
8876   HDC hdc;\r
8877   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8878 \r
8879   if(appData.noGUI) return;\r
8880   hdc = GetDC(hwndMain);\r
8881   if (!IsIconic(hwndMain)) {\r
8882     DisplayAClock(hdc, timeRemaining, highlight, \r
8883                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8884   }\r
8885   if (highlight && iconCurrent == iconBlack) {\r
8886     iconCurrent = iconWhite;\r
8887     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8888     if (IsIconic(hwndMain)) {\r
8889       DrawIcon(hdc, 2, 2, iconCurrent);\r
8890     }\r
8891   }\r
8892   (void) ReleaseDC(hwndMain, hdc);\r
8893   if (hwndConsole)\r
8894     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8895 }\r
8896 \r
8897 void\r
8898 DisplayBlackClock(long timeRemaining, int highlight)\r
8899 {\r
8900   HDC hdc;\r
8901   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8902 \r
8903   if(appData.noGUI) return;\r
8904   hdc = GetDC(hwndMain);\r
8905   if (!IsIconic(hwndMain)) {\r
8906     DisplayAClock(hdc, timeRemaining, highlight, \r
8907                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8908   }\r
8909   if (highlight && iconCurrent == iconWhite) {\r
8910     iconCurrent = iconBlack;\r
8911     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8912     if (IsIconic(hwndMain)) {\r
8913       DrawIcon(hdc, 2, 2, iconCurrent);\r
8914     }\r
8915   }\r
8916   (void) ReleaseDC(hwndMain, hdc);\r
8917   if (hwndConsole)\r
8918     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8919 }\r
8920 \r
8921 \r
8922 int\r
8923 LoadGameTimerRunning()\r
8924 {\r
8925   return loadGameTimerEvent != 0;\r
8926 }\r
8927 \r
8928 int\r
8929 StopLoadGameTimer()\r
8930 {\r
8931   if (loadGameTimerEvent == 0) return FALSE;\r
8932   KillTimer(hwndMain, loadGameTimerEvent);\r
8933   loadGameTimerEvent = 0;\r
8934   return TRUE;\r
8935 }\r
8936 \r
8937 void\r
8938 StartLoadGameTimer(long millisec)\r
8939 {\r
8940   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8941                                 (UINT) millisec, NULL);\r
8942 }\r
8943 \r
8944 void\r
8945 AutoSaveGame()\r
8946 {\r
8947   char *defName;\r
8948   FILE *f;\r
8949   char fileTitle[MSG_SIZ];\r
8950 \r
8951   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8952   f = OpenFileDialog(hwndMain, "a", defName,\r
8953                      appData.oldSaveStyle ? "gam" : "pgn",\r
8954                      GAME_FILT, \r
8955                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8956   if (f != NULL) {\r
8957     SaveGame(f, 0, "");\r
8958     fclose(f);\r
8959   }\r
8960 }\r
8961 \r
8962 \r
8963 void\r
8964 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8965 {\r
8966   if (delayedTimerEvent != 0) {\r
8967     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8968       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8969     }\r
8970     KillTimer(hwndMain, delayedTimerEvent);\r
8971     delayedTimerEvent = 0;\r
8972     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8973     delayedTimerCallback();\r
8974   }\r
8975   delayedTimerCallback = cb;\r
8976   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8977                                 (UINT) millisec, NULL);\r
8978 }\r
8979 \r
8980 DelayedEventCallback\r
8981 GetDelayedEvent()\r
8982 {\r
8983   if (delayedTimerEvent) {\r
8984     return delayedTimerCallback;\r
8985   } else {\r
8986     return NULL;\r
8987   }\r
8988 }\r
8989 \r
8990 void\r
8991 CancelDelayedEvent()\r
8992 {\r
8993   if (delayedTimerEvent) {\r
8994     KillTimer(hwndMain, delayedTimerEvent);\r
8995     delayedTimerEvent = 0;\r
8996   }\r
8997 }\r
8998 \r
8999 DWORD GetWin32Priority(int nice)\r
9000 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9001 /*\r
9002 REALTIME_PRIORITY_CLASS     0x00000100\r
9003 HIGH_PRIORITY_CLASS         0x00000080\r
9004 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9005 NORMAL_PRIORITY_CLASS       0x00000020\r
9006 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9007 IDLE_PRIORITY_CLASS         0x00000040\r
9008 */\r
9009         if (nice < -15) return 0x00000080;\r
9010         if (nice < 0)   return 0x00008000;\r
9011         if (nice == 0)  return 0x00000020;\r
9012         if (nice < 15)  return 0x00004000;\r
9013         return 0x00000040;\r
9014 }\r
9015 \r
9016 void RunCommand(char *cmdLine)\r
9017 {\r
9018   /* Now create the child process. */\r
9019   STARTUPINFO siStartInfo;\r
9020   PROCESS_INFORMATION piProcInfo;\r
9021 \r
9022   siStartInfo.cb = sizeof(STARTUPINFO);\r
9023   siStartInfo.lpReserved = NULL;\r
9024   siStartInfo.lpDesktop = NULL;\r
9025   siStartInfo.lpTitle = NULL;\r
9026   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9027   siStartInfo.cbReserved2 = 0;\r
9028   siStartInfo.lpReserved2 = NULL;\r
9029   siStartInfo.hStdInput = NULL;\r
9030   siStartInfo.hStdOutput = NULL;\r
9031   siStartInfo.hStdError = NULL;\r
9032 \r
9033   CreateProcess(NULL,\r
9034                 cmdLine,           /* command line */\r
9035                 NULL,      /* process security attributes */\r
9036                 NULL,      /* primary thread security attrs */\r
9037                 TRUE,      /* handles are inherited */\r
9038                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9039                 NULL,      /* use parent's environment */\r
9040                 NULL,\r
9041                 &siStartInfo, /* STARTUPINFO pointer */\r
9042                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9043 \r
9044   CloseHandle(piProcInfo.hThread);\r
9045 }\r
9046 \r
9047 /* Start a child process running the given program.\r
9048    The process's standard output can be read from "from", and its\r
9049    standard input can be written to "to".\r
9050    Exit with fatal error if anything goes wrong.\r
9051    Returns an opaque pointer that can be used to destroy the process\r
9052    later.\r
9053 */\r
9054 int\r
9055 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9056 {\r
9057 #define BUFSIZE 4096\r
9058 \r
9059   HANDLE hChildStdinRd, hChildStdinWr,\r
9060     hChildStdoutRd, hChildStdoutWr;\r
9061   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9062   SECURITY_ATTRIBUTES saAttr;\r
9063   BOOL fSuccess;\r
9064   PROCESS_INFORMATION piProcInfo;\r
9065   STARTUPINFO siStartInfo;\r
9066   ChildProc *cp;\r
9067   char buf[MSG_SIZ];\r
9068   DWORD err;\r
9069 \r
9070   if (appData.debugMode) {\r
9071     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9072   }\r
9073 \r
9074   *pr = NoProc;\r
9075 \r
9076   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9077   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9078   saAttr.bInheritHandle = TRUE;\r
9079   saAttr.lpSecurityDescriptor = NULL;\r
9080 \r
9081   /*\r
9082    * The steps for redirecting child's STDOUT:\r
9083    *     1. Create anonymous pipe to be STDOUT for child.\r
9084    *     2. Create a noninheritable duplicate of read handle,\r
9085    *         and close the inheritable read handle.\r
9086    */\r
9087 \r
9088   /* Create a pipe for the child's STDOUT. */\r
9089   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9090     return GetLastError();\r
9091   }\r
9092 \r
9093   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9094   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9095                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9096                              FALSE,     /* not inherited */\r
9097                              DUPLICATE_SAME_ACCESS);\r
9098   if (! fSuccess) {\r
9099     return GetLastError();\r
9100   }\r
9101   CloseHandle(hChildStdoutRd);\r
9102 \r
9103   /*\r
9104    * The steps for redirecting child's STDIN:\r
9105    *     1. Create anonymous pipe to be STDIN for child.\r
9106    *     2. Create a noninheritable duplicate of write handle,\r
9107    *         and close the inheritable write handle.\r
9108    */\r
9109 \r
9110   /* Create a pipe for the child's STDIN. */\r
9111   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9112     return GetLastError();\r
9113   }\r
9114 \r
9115   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9116   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9117                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9118                              FALSE,     /* not inherited */\r
9119                              DUPLICATE_SAME_ACCESS);\r
9120   if (! fSuccess) {\r
9121     return GetLastError();\r
9122   }\r
9123   CloseHandle(hChildStdinWr);\r
9124 \r
9125   /* Arrange to (1) look in dir for the child .exe file, and\r
9126    * (2) have dir be the child's working directory.  Interpret\r
9127    * dir relative to the directory WinBoard loaded from. */\r
9128   GetCurrentDirectory(MSG_SIZ, buf);\r
9129   SetCurrentDirectory(installDir);\r
9130   SetCurrentDirectory(dir);\r
9131 \r
9132   /* Now create the child process. */\r
9133 \r
9134   siStartInfo.cb = sizeof(STARTUPINFO);\r
9135   siStartInfo.lpReserved = NULL;\r
9136   siStartInfo.lpDesktop = NULL;\r
9137   siStartInfo.lpTitle = NULL;\r
9138   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9139   siStartInfo.cbReserved2 = 0;\r
9140   siStartInfo.lpReserved2 = NULL;\r
9141   siStartInfo.hStdInput = hChildStdinRd;\r
9142   siStartInfo.hStdOutput = hChildStdoutWr;\r
9143   siStartInfo.hStdError = hChildStdoutWr;\r
9144 \r
9145   fSuccess = CreateProcess(NULL,\r
9146                            cmdLine,        /* command line */\r
9147                            NULL,           /* process security attributes */\r
9148                            NULL,           /* primary thread security attrs */\r
9149                            TRUE,           /* handles are inherited */\r
9150                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9151                            NULL,           /* use parent's environment */\r
9152                            NULL,\r
9153                            &siStartInfo, /* STARTUPINFO pointer */\r
9154                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9155 \r
9156   err = GetLastError();\r
9157   SetCurrentDirectory(buf); /* return to prev directory */\r
9158   if (! fSuccess) {\r
9159     return err;\r
9160   }\r
9161 \r
9162   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9163     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9164     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9165   }\r
9166 \r
9167   /* Close the handles we don't need in the parent */\r
9168   CloseHandle(piProcInfo.hThread);\r
9169   CloseHandle(hChildStdinRd);\r
9170   CloseHandle(hChildStdoutWr);\r
9171 \r
9172   /* Prepare return value */\r
9173   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9174   cp->kind = CPReal;\r
9175   cp->hProcess = piProcInfo.hProcess;\r
9176   cp->pid = piProcInfo.dwProcessId;\r
9177   cp->hFrom = hChildStdoutRdDup;\r
9178   cp->hTo = hChildStdinWrDup;\r
9179 \r
9180   *pr = (void *) cp;\r
9181 \r
9182   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9183      2000 where engines sometimes don't see the initial command(s)\r
9184      from WinBoard and hang.  I don't understand how that can happen,\r
9185      but the Sleep is harmless, so I've put it in.  Others have also\r
9186      reported what may be the same problem, so hopefully this will fix\r
9187      it for them too.  */\r
9188   Sleep(500);\r
9189 \r
9190   return NO_ERROR;\r
9191 }\r
9192 \r
9193 \r
9194 void\r
9195 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9196 {\r
9197   ChildProc *cp; int result;\r
9198 \r
9199   cp = (ChildProc *) pr;\r
9200   if (cp == NULL) return;\r
9201 \r
9202   switch (cp->kind) {\r
9203   case CPReal:\r
9204     /* TerminateProcess is considered harmful, so... */\r
9205     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9206     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9207     /* The following doesn't work because the chess program\r
9208        doesn't "have the same console" as WinBoard.  Maybe\r
9209        we could arrange for this even though neither WinBoard\r
9210        nor the chess program uses a console for stdio? */\r
9211     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9212 \r
9213     /* [AS] Special termination modes for misbehaving programs... */\r
9214     if( signal == 9 ) { \r
9215         result = TerminateProcess( cp->hProcess, 0 );\r
9216 \r
9217         if ( appData.debugMode) {\r
9218             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9219         }\r
9220     }\r
9221     else if( signal == 10 ) {\r
9222         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9223 \r
9224         if( dw != WAIT_OBJECT_0 ) {\r
9225             result = TerminateProcess( cp->hProcess, 0 );\r
9226 \r
9227             if ( appData.debugMode) {\r
9228                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9229             }\r
9230 \r
9231         }\r
9232     }\r
9233 \r
9234     CloseHandle(cp->hProcess);\r
9235     break;\r
9236 \r
9237   case CPComm:\r
9238     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9239     break;\r
9240 \r
9241   case CPSock:\r
9242     closesocket(cp->sock);\r
9243     WSACleanup();\r
9244     break;\r
9245 \r
9246   case CPRcmd:\r
9247     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9248     closesocket(cp->sock);\r
9249     closesocket(cp->sock2);\r
9250     WSACleanup();\r
9251     break;\r
9252   }\r
9253   free(cp);\r
9254 }\r
9255 \r
9256 void\r
9257 InterruptChildProcess(ProcRef pr)\r
9258 {\r
9259   ChildProc *cp;\r
9260 \r
9261   cp = (ChildProc *) pr;\r
9262   if (cp == NULL) return;\r
9263   switch (cp->kind) {\r
9264   case CPReal:\r
9265     /* The following doesn't work because the chess program\r
9266        doesn't "have the same console" as WinBoard.  Maybe\r
9267        we could arrange for this even though neither WinBoard\r
9268        nor the chess program uses a console for stdio */\r
9269     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9270     break;\r
9271 \r
9272   case CPComm:\r
9273   case CPSock:\r
9274     /* Can't interrupt */\r
9275     break;\r
9276 \r
9277   case CPRcmd:\r
9278     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9279     break;\r
9280   }\r
9281 }\r
9282 \r
9283 \r
9284 int\r
9285 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9286 {\r
9287   char cmdLine[MSG_SIZ];\r
9288 \r
9289   if (port[0] == NULLCHAR) {\r
9290     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9291   } else {\r
9292     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9293   }\r
9294   return StartChildProcess(cmdLine, "", pr);\r
9295 }\r
9296 \r
9297 \r
9298 /* Code to open TCP sockets */\r
9299 \r
9300 int\r
9301 OpenTCP(char *host, char *port, ProcRef *pr)\r
9302 {\r
9303   ChildProc *cp;\r
9304   int err;\r
9305   SOCKET s;\r
9306 \r
9307   struct sockaddr_in sa, mysa;\r
9308   struct hostent FAR *hp;\r
9309   unsigned short uport;\r
9310   WORD wVersionRequested;\r
9311   WSADATA wsaData;\r
9312 \r
9313   /* Initialize socket DLL */\r
9314   wVersionRequested = MAKEWORD(1, 1);\r
9315   err = WSAStartup(wVersionRequested, &wsaData);\r
9316   if (err != 0) return err;\r
9317 \r
9318   /* Make socket */\r
9319   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9320     err = WSAGetLastError();\r
9321     WSACleanup();\r
9322     return err;\r
9323   }\r
9324 \r
9325   /* Bind local address using (mostly) don't-care values.\r
9326    */\r
9327   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9328   mysa.sin_family = AF_INET;\r
9329   mysa.sin_addr.s_addr = INADDR_ANY;\r
9330   uport = (unsigned short) 0;\r
9331   mysa.sin_port = htons(uport);\r
9332   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9333       == SOCKET_ERROR) {\r
9334     err = WSAGetLastError();\r
9335     WSACleanup();\r
9336     return err;\r
9337   }\r
9338 \r
9339   /* Resolve remote host name */\r
9340   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9341   if (!(hp = gethostbyname(host))) {\r
9342     unsigned int b0, b1, b2, b3;\r
9343 \r
9344     err = WSAGetLastError();\r
9345 \r
9346     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9347       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9348       hp->h_addrtype = AF_INET;\r
9349       hp->h_length = 4;\r
9350       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9351       hp->h_addr_list[0] = (char *) malloc(4);\r
9352       hp->h_addr_list[0][0] = (char) b0;\r
9353       hp->h_addr_list[0][1] = (char) b1;\r
9354       hp->h_addr_list[0][2] = (char) b2;\r
9355       hp->h_addr_list[0][3] = (char) b3;\r
9356     } else {\r
9357       WSACleanup();\r
9358       return err;\r
9359     }\r
9360   }\r
9361   sa.sin_family = hp->h_addrtype;\r
9362   uport = (unsigned short) atoi(port);\r
9363   sa.sin_port = htons(uport);\r
9364   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9365 \r
9366   /* Make connection */\r
9367   if (connect(s, (struct sockaddr *) &sa,\r
9368               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9369     err = WSAGetLastError();\r
9370     WSACleanup();\r
9371     return err;\r
9372   }\r
9373 \r
9374   /* Prepare return value */\r
9375   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9376   cp->kind = CPSock;\r
9377   cp->sock = s;\r
9378   *pr = (ProcRef *) cp;\r
9379 \r
9380   return NO_ERROR;\r
9381 }\r
9382 \r
9383 int\r
9384 OpenCommPort(char *name, ProcRef *pr)\r
9385 {\r
9386   HANDLE h;\r
9387   COMMTIMEOUTS ct;\r
9388   ChildProc *cp;\r
9389   char fullname[MSG_SIZ];\r
9390 \r
9391   if (*name != '\\')\r
9392     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9393   else\r
9394     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9395 \r
9396   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9397                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9398   if (h == (HANDLE) -1) {\r
9399     return GetLastError();\r
9400   }\r
9401   hCommPort = h;\r
9402 \r
9403   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9404 \r
9405   /* Accumulate characters until a 100ms pause, then parse */\r
9406   ct.ReadIntervalTimeout = 100;\r
9407   ct.ReadTotalTimeoutMultiplier = 0;\r
9408   ct.ReadTotalTimeoutConstant = 0;\r
9409   ct.WriteTotalTimeoutMultiplier = 0;\r
9410   ct.WriteTotalTimeoutConstant = 0;\r
9411   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9412 \r
9413   /* Prepare return value */\r
9414   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9415   cp->kind = CPComm;\r
9416   cp->hFrom = h;\r
9417   cp->hTo = h;\r
9418   *pr = (ProcRef *) cp;\r
9419 \r
9420   return NO_ERROR;\r
9421 }\r
9422 \r
9423 int\r
9424 OpenLoopback(ProcRef *pr)\r
9425 {\r
9426   DisplayFatalError(_("Not implemented"), 0, 1);\r
9427   return NO_ERROR;\r
9428 }\r
9429 \r
9430 \r
9431 int\r
9432 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9433 {\r
9434   ChildProc *cp;\r
9435   int err;\r
9436   SOCKET s, s2, s3;\r
9437   struct sockaddr_in sa, mysa;\r
9438   struct hostent FAR *hp;\r
9439   unsigned short uport;\r
9440   WORD wVersionRequested;\r
9441   WSADATA wsaData;\r
9442   int fromPort;\r
9443   char stderrPortStr[MSG_SIZ];\r
9444 \r
9445   /* Initialize socket DLL */\r
9446   wVersionRequested = MAKEWORD(1, 1);\r
9447   err = WSAStartup(wVersionRequested, &wsaData);\r
9448   if (err != 0) return err;\r
9449 \r
9450   /* Resolve remote host name */\r
9451   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9452   if (!(hp = gethostbyname(host))) {\r
9453     unsigned int b0, b1, b2, b3;\r
9454 \r
9455     err = WSAGetLastError();\r
9456 \r
9457     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9458       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9459       hp->h_addrtype = AF_INET;\r
9460       hp->h_length = 4;\r
9461       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9462       hp->h_addr_list[0] = (char *) malloc(4);\r
9463       hp->h_addr_list[0][0] = (char) b0;\r
9464       hp->h_addr_list[0][1] = (char) b1;\r
9465       hp->h_addr_list[0][2] = (char) b2;\r
9466       hp->h_addr_list[0][3] = (char) b3;\r
9467     } else {\r
9468       WSACleanup();\r
9469       return err;\r
9470     }\r
9471   }\r
9472   sa.sin_family = hp->h_addrtype;\r
9473   uport = (unsigned short) 514;\r
9474   sa.sin_port = htons(uport);\r
9475   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9476 \r
9477   /* Bind local socket to unused "privileged" port address\r
9478    */\r
9479   s = INVALID_SOCKET;\r
9480   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9481   mysa.sin_family = AF_INET;\r
9482   mysa.sin_addr.s_addr = INADDR_ANY;\r
9483   for (fromPort = 1023;; fromPort--) {\r
9484     if (fromPort < 0) {\r
9485       WSACleanup();\r
9486       return WSAEADDRINUSE;\r
9487     }\r
9488     if (s == INVALID_SOCKET) {\r
9489       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9490         err = WSAGetLastError();\r
9491         WSACleanup();\r
9492         return err;\r
9493       }\r
9494     }\r
9495     uport = (unsigned short) fromPort;\r
9496     mysa.sin_port = htons(uport);\r
9497     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9498         == SOCKET_ERROR) {\r
9499       err = WSAGetLastError();\r
9500       if (err == WSAEADDRINUSE) continue;\r
9501       WSACleanup();\r
9502       return err;\r
9503     }\r
9504     if (connect(s, (struct sockaddr *) &sa,\r
9505       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9506       err = WSAGetLastError();\r
9507       if (err == WSAEADDRINUSE) {\r
9508         closesocket(s);\r
9509         s = -1;\r
9510         continue;\r
9511       }\r
9512       WSACleanup();\r
9513       return err;\r
9514     }\r
9515     break;\r
9516   }\r
9517 \r
9518   /* Bind stderr local socket to unused "privileged" port address\r
9519    */\r
9520   s2 = INVALID_SOCKET;\r
9521   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9522   mysa.sin_family = AF_INET;\r
9523   mysa.sin_addr.s_addr = INADDR_ANY;\r
9524   for (fromPort = 1023;; fromPort--) {\r
9525     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9526     if (fromPort < 0) {\r
9527       (void) closesocket(s);\r
9528       WSACleanup();\r
9529       return WSAEADDRINUSE;\r
9530     }\r
9531     if (s2 == INVALID_SOCKET) {\r
9532       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9533         err = WSAGetLastError();\r
9534         closesocket(s);\r
9535         WSACleanup();\r
9536         return err;\r
9537       }\r
9538     }\r
9539     uport = (unsigned short) fromPort;\r
9540     mysa.sin_port = htons(uport);\r
9541     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9542         == SOCKET_ERROR) {\r
9543       err = WSAGetLastError();\r
9544       if (err == WSAEADDRINUSE) continue;\r
9545       (void) closesocket(s);\r
9546       WSACleanup();\r
9547       return err;\r
9548     }\r
9549     if (listen(s2, 1) == SOCKET_ERROR) {\r
9550       err = WSAGetLastError();\r
9551       if (err == WSAEADDRINUSE) {\r
9552         closesocket(s2);\r
9553         s2 = INVALID_SOCKET;\r
9554         continue;\r
9555       }\r
9556       (void) closesocket(s);\r
9557       (void) closesocket(s2);\r
9558       WSACleanup();\r
9559       return err;\r
9560     }\r
9561     break;\r
9562   }\r
9563   prevStderrPort = fromPort; // remember port used\r
9564   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9565 \r
9566   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9567     err = WSAGetLastError();\r
9568     (void) closesocket(s);\r
9569     (void) closesocket(s2);\r
9570     WSACleanup();\r
9571     return err;\r
9572   }\r
9573 \r
9574   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9575     err = WSAGetLastError();\r
9576     (void) closesocket(s);\r
9577     (void) closesocket(s2);\r
9578     WSACleanup();\r
9579     return err;\r
9580   }\r
9581   if (*user == NULLCHAR) user = UserName();\r
9582   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9583     err = WSAGetLastError();\r
9584     (void) closesocket(s);\r
9585     (void) closesocket(s2);\r
9586     WSACleanup();\r
9587     return err;\r
9588   }\r
9589   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9590     err = WSAGetLastError();\r
9591     (void) closesocket(s);\r
9592     (void) closesocket(s2);\r
9593     WSACleanup();\r
9594     return err;\r
9595   }\r
9596 \r
9597   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9598     err = WSAGetLastError();\r
9599     (void) closesocket(s);\r
9600     (void) closesocket(s2);\r
9601     WSACleanup();\r
9602     return err;\r
9603   }\r
9604   (void) closesocket(s2);  /* Stop listening */\r
9605 \r
9606   /* Prepare return value */\r
9607   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9608   cp->kind = CPRcmd;\r
9609   cp->sock = s;\r
9610   cp->sock2 = s3;\r
9611   *pr = (ProcRef *) cp;\r
9612 \r
9613   return NO_ERROR;\r
9614 }\r
9615 \r
9616 \r
9617 InputSourceRef\r
9618 AddInputSource(ProcRef pr, int lineByLine,\r
9619                InputCallback func, VOIDSTAR closure)\r
9620 {\r
9621   InputSource *is, *is2 = NULL;\r
9622   ChildProc *cp = (ChildProc *) pr;\r
9623 \r
9624   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9625   is->lineByLine = lineByLine;\r
9626   is->func = func;\r
9627   is->closure = closure;\r
9628   is->second = NULL;\r
9629   is->next = is->buf;\r
9630   if (pr == NoProc) {\r
9631     is->kind = CPReal;\r
9632     consoleInputSource = is;\r
9633   } else {\r
9634     is->kind = cp->kind;\r
9635     /* \r
9636         [AS] Try to avoid a race condition if the thread is given control too early:\r
9637         we create all threads suspended so that the is->hThread variable can be\r
9638         safely assigned, then let the threads start with ResumeThread.\r
9639     */\r
9640     switch (cp->kind) {\r
9641     case CPReal:\r
9642       is->hFile = cp->hFrom;\r
9643       cp->hFrom = NULL; /* now owned by InputThread */\r
9644       is->hThread =\r
9645         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9646                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9647       break;\r
9648 \r
9649     case CPComm:\r
9650       is->hFile = cp->hFrom;\r
9651       cp->hFrom = NULL; /* now owned by InputThread */\r
9652       is->hThread =\r
9653         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9654                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9655       break;\r
9656 \r
9657     case CPSock:\r
9658       is->sock = cp->sock;\r
9659       is->hThread =\r
9660         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9661                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9662       break;\r
9663 \r
9664     case CPRcmd:\r
9665       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9666       *is2 = *is;\r
9667       is->sock = cp->sock;\r
9668       is->second = is2;\r
9669       is2->sock = cp->sock2;\r
9670       is2->second = is2;\r
9671       is->hThread =\r
9672         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9673                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9674       is2->hThread =\r
9675         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9676                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9677       break;\r
9678     }\r
9679 \r
9680     if( is->hThread != NULL ) {\r
9681         ResumeThread( is->hThread );\r
9682     }\r
9683 \r
9684     if( is2 != NULL && is2->hThread != NULL ) {\r
9685         ResumeThread( is2->hThread );\r
9686     }\r
9687   }\r
9688 \r
9689   return (InputSourceRef) is;\r
9690 }\r
9691 \r
9692 void\r
9693 RemoveInputSource(InputSourceRef isr)\r
9694 {\r
9695   InputSource *is;\r
9696 \r
9697   is = (InputSource *) isr;\r
9698   is->hThread = NULL;  /* tell thread to stop */\r
9699   CloseHandle(is->hThread);\r
9700   if (is->second != NULL) {\r
9701     is->second->hThread = NULL;\r
9702     CloseHandle(is->second->hThread);\r
9703   }\r
9704 }\r
9705 \r
9706 int no_wrap(char *message, int count)\r
9707 {\r
9708     ConsoleOutput(message, count, FALSE);\r
9709     return count;\r
9710 }\r
9711 \r
9712 int\r
9713 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9714 {\r
9715   DWORD dOutCount;\r
9716   int outCount = SOCKET_ERROR;\r
9717   ChildProc *cp = (ChildProc *) pr;\r
9718   static OVERLAPPED ovl;\r
9719   static int line = 0;\r
9720 \r
9721   if (pr == NoProc)\r
9722   {\r
9723     if (appData.noJoin || !appData.useInternalWrap)\r
9724       return no_wrap(message, count);\r
9725     else\r
9726     {\r
9727       int width = get_term_width();\r
9728       int len = wrap(NULL, message, count, width, &line);\r
9729       char *msg = malloc(len);\r
9730       int dbgchk;\r
9731 \r
9732       if (!msg)\r
9733         return no_wrap(message, count);\r
9734       else\r
9735       {\r
9736         dbgchk = wrap(msg, message, count, width, &line);\r
9737         if (dbgchk != len && appData.debugMode)\r
9738             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9739         ConsoleOutput(msg, len, FALSE);\r
9740         free(msg);\r
9741         return len;\r
9742       }\r
9743     }\r
9744   }\r
9745 \r
9746   if (ovl.hEvent == NULL) {\r
9747     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9748   }\r
9749   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9750 \r
9751   switch (cp->kind) {\r
9752   case CPSock:\r
9753   case CPRcmd:\r
9754     outCount = send(cp->sock, message, count, 0);\r
9755     if (outCount == SOCKET_ERROR) {\r
9756       *outError = WSAGetLastError();\r
9757     } else {\r
9758       *outError = NO_ERROR;\r
9759     }\r
9760     break;\r
9761 \r
9762   case CPReal:\r
9763     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9764                   &dOutCount, NULL)) {\r
9765       *outError = NO_ERROR;\r
9766       outCount = (int) dOutCount;\r
9767     } else {\r
9768       *outError = GetLastError();\r
9769     }\r
9770     break;\r
9771 \r
9772   case CPComm:\r
9773     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9774                             &dOutCount, &ovl);\r
9775     if (*outError == NO_ERROR) {\r
9776       outCount = (int) dOutCount;\r
9777     }\r
9778     break;\r
9779   }\r
9780   return outCount;\r
9781 }\r
9782 \r
9783 void\r
9784 DoSleep(int n)\r
9785 {\r
9786     if(n != 0) Sleep(n);\r
9787 }\r
9788 \r
9789 int\r
9790 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9791                        long msdelay)\r
9792 {\r
9793   /* Ignore delay, not implemented for WinBoard */\r
9794   return OutputToProcess(pr, message, count, outError);\r
9795 }\r
9796 \r
9797 \r
9798 void\r
9799 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9800                         char *buf, int count, int error)\r
9801 {\r
9802   DisplayFatalError(_("Not implemented"), 0, 1);\r
9803 }\r
9804 \r
9805 /* see wgamelist.c for Game List functions */\r
9806 /* see wedittags.c for Edit Tags functions */\r
9807 \r
9808 \r
9809 int\r
9810 ICSInitScript()\r
9811 {\r
9812   FILE *f;\r
9813   char buf[MSG_SIZ];\r
9814   char *dummy;\r
9815 \r
9816   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9817     f = fopen(buf, "r");\r
9818     if (f != NULL) {\r
9819       ProcessICSInitScript(f);\r
9820       fclose(f);\r
9821       return TRUE;\r
9822     }\r
9823   }\r
9824   return FALSE;\r
9825 }\r
9826 \r
9827 \r
9828 VOID\r
9829 StartAnalysisClock()\r
9830 {\r
9831   if (analysisTimerEvent) return;\r
9832   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9833                                         (UINT) 2000, NULL);\r
9834 }\r
9835 \r
9836 VOID\r
9837 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9838 {\r
9839   highlightInfo.sq[0].x = fromX;\r
9840   highlightInfo.sq[0].y = fromY;\r
9841   highlightInfo.sq[1].x = toX;\r
9842   highlightInfo.sq[1].y = toY;\r
9843 }\r
9844 \r
9845 VOID\r
9846 ClearHighlights()\r
9847 {\r
9848   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9849     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9850 }\r
9851 \r
9852 VOID\r
9853 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9854 {\r
9855   premoveHighlightInfo.sq[0].x = fromX;\r
9856   premoveHighlightInfo.sq[0].y = fromY;\r
9857   premoveHighlightInfo.sq[1].x = toX;\r
9858   premoveHighlightInfo.sq[1].y = toY;\r
9859 }\r
9860 \r
9861 VOID\r
9862 ClearPremoveHighlights()\r
9863 {\r
9864   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9865     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9866 }\r
9867 \r
9868 VOID\r
9869 ShutDownFrontEnd()\r
9870 {\r
9871   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9872   DeleteClipboardTempFiles();\r
9873 }\r
9874 \r
9875 void\r
9876 BoardToTop()\r
9877 {\r
9878     if (IsIconic(hwndMain))\r
9879       ShowWindow(hwndMain, SW_RESTORE);\r
9880 \r
9881     SetActiveWindow(hwndMain);\r
9882 }\r
9883 \r
9884 /*\r
9885  * Prototypes for animation support routines\r
9886  */\r
9887 static void ScreenSquare(int column, int row, POINT * pt);\r
9888 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9889      POINT frames[], int * nFrames);\r
9890 \r
9891 \r
9892 #define kFactor 4\r
9893 \r
9894 void\r
9895 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9896 {       // [HGM] atomic: animate blast wave\r
9897         int i;\r
9898 \r
9899         explodeInfo.fromX = fromX;\r
9900         explodeInfo.fromY = fromY;\r
9901         explodeInfo.toX = toX;\r
9902         explodeInfo.toY = toY;\r
9903         for(i=1; i<4*kFactor; i++) {\r
9904             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9905             DrawPosition(FALSE, board);\r
9906             Sleep(appData.animSpeed);\r
9907         }\r
9908         explodeInfo.radius = 0;\r
9909         DrawPosition(TRUE, board);\r
9910 }\r
9911 \r
9912 void\r
9913 AnimateMove(board, fromX, fromY, toX, toY)\r
9914      Board board;\r
9915      int fromX;\r
9916      int fromY;\r
9917      int toX;\r
9918      int toY;\r
9919 {\r
9920   ChessSquare piece;\r
9921   POINT start, finish, mid;\r
9922   POINT frames[kFactor * 2 + 1];\r
9923   int nFrames, n;\r
9924 \r
9925   if (!appData.animate) return;\r
9926   if (doingSizing) return;\r
9927   if (fromY < 0 || fromX < 0) return;\r
9928   piece = board[fromY][fromX];\r
9929   if (piece >= EmptySquare) return;\r
9930 \r
9931   ScreenSquare(fromX, fromY, &start);\r
9932   ScreenSquare(toX, toY, &finish);\r
9933 \r
9934   /* All moves except knight jumps move in straight line */\r
9935   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9936     mid.x = start.x + (finish.x - start.x) / 2;\r
9937     mid.y = start.y + (finish.y - start.y) / 2;\r
9938   } else {\r
9939     /* Knight: make straight movement then diagonal */\r
9940     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9941        mid.x = start.x + (finish.x - start.x) / 2;\r
9942        mid.y = start.y;\r
9943      } else {\r
9944        mid.x = start.x;\r
9945        mid.y = start.y + (finish.y - start.y) / 2;\r
9946      }\r
9947   }\r
9948   \r
9949   /* Don't use as many frames for very short moves */\r
9950   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9951     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9952   else\r
9953     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9954 \r
9955   animInfo.from.x = fromX;\r
9956   animInfo.from.y = fromY;\r
9957   animInfo.to.x = toX;\r
9958   animInfo.to.y = toY;\r
9959   animInfo.lastpos = start;\r
9960   animInfo.piece = piece;\r
9961   for (n = 0; n < nFrames; n++) {\r
9962     animInfo.pos = frames[n];\r
9963     DrawPosition(FALSE, NULL);\r
9964     animInfo.lastpos = animInfo.pos;\r
9965     Sleep(appData.animSpeed);\r
9966   }\r
9967   animInfo.pos = finish;\r
9968   DrawPosition(FALSE, NULL);\r
9969   animInfo.piece = EmptySquare;\r
9970   Explode(board, fromX, fromY, toX, toY);\r
9971 }\r
9972 \r
9973 /*      Convert board position to corner of screen rect and color       */\r
9974 \r
9975 static void\r
9976 ScreenSquare(column, row, pt)\r
9977      int column; int row; POINT * pt;\r
9978 {\r
9979   if (flipView) {\r
9980     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9981     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9982   } else {\r
9983     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9984     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9985   }\r
9986 }\r
9987 \r
9988 /*      Generate a series of frame coords from start->mid->finish.\r
9989         The movement rate doubles until the half way point is\r
9990         reached, then halves back down to the final destination,\r
9991         which gives a nice slow in/out effect. The algorithmn\r
9992         may seem to generate too many intermediates for short\r
9993         moves, but remember that the purpose is to attract the\r
9994         viewers attention to the piece about to be moved and\r
9995         then to where it ends up. Too few frames would be less\r
9996         noticeable.                                             */\r
9997 \r
9998 static void\r
9999 Tween(start, mid, finish, factor, frames, nFrames)\r
10000      POINT * start; POINT * mid;\r
10001      POINT * finish; int factor;\r
10002      POINT frames[]; int * nFrames;\r
10003 {\r
10004   int n, fraction = 1, count = 0;\r
10005 \r
10006   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10007   for (n = 0; n < factor; n++)\r
10008     fraction *= 2;\r
10009   for (n = 0; n < factor; n++) {\r
10010     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10011     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10012     count ++;\r
10013     fraction = fraction / 2;\r
10014   }\r
10015   \r
10016   /* Midpoint */\r
10017   frames[count] = *mid;\r
10018   count ++;\r
10019   \r
10020   /* Slow out, stepping 1/2, then 1/4, ... */\r
10021   fraction = 2;\r
10022   for (n = 0; n < factor; n++) {\r
10023     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10024     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10025     count ++;\r
10026     fraction = fraction * 2;\r
10027   }\r
10028   *nFrames = count;\r
10029 }\r
10030 \r
10031 void\r
10032 SettingsPopUp(ChessProgramState *cps)\r
10033 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10034       EngineOptionsPopup(savedHwnd, cps);\r
10035 }\r
10036 \r
10037 int flock(int fid, int code)\r
10038 {\r
10039     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10040     OVERLAPPED ov;\r
10041     ov.hEvent = NULL;\r
10042     ov.Offset = 0;\r
10043     ov.OffsetHigh = 0;\r
10044     switch(code) {\r
10045       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10046       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10047       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10048       default: return -1;\r
10049     }\r
10050     return 0;\r
10051 }\r
10052 \r
10053 char *\r
10054 Col2Text (int n)\r
10055 {\r
10056     static int i=0;\r
10057     static char col[8][20];\r
10058     COLORREF color = *(COLORREF *) colorVariable[n];\r
10059     i = i+1 & 7;\r
10060     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10061     return col[i];\r
10062 }\r
10063 \r
10064 void\r
10065 ActivateTheme (int new)\r
10066 {   // Redo initialization of features depending on options that can occur in themes\r
10067    InitTextures();\r
10068    if(new) InitDrawingColors();\r
10069    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10070    InitDrawingSizes(boardSize, 0);\r
10071    InvalidateRect(hwndMain, NULL, TRUE);\r
10072 }\r