Fix buffer overflow in parser
[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 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 \r
226 \r
227 #if defined(_winmajor)\r
228 #define oldDialog (_winmajor < 4)\r
229 #else\r
230 #define oldDialog 0\r
231 #endif\r
232 #endif\r
233 \r
234 #define INTERNATIONAL\r
235 \r
236 #ifdef INTERNATIONAL\r
237 #  define _(s) T_(s)\r
238 #  define N_(s) s\r
239 #else\r
240 #  define _(s) s\r
241 #  define N_(s) s\r
242 #  define T_(s) s\r
243 #  define Translate(x, y)\r
244 #  define LoadLanguageFile(s)\r
245 #endif\r
246 \r
247 #ifdef INTERNATIONAL\r
248 \r
249 Boolean barbaric; // flag indicating if translation is needed\r
250 \r
251 // list of item numbers used in each dialog (used to alter language at run time)\r
252 \r
253 #define ABOUTBOX -1  /* not sure why these are needed */\r
254 #define ABOUTBOX2 -1\r
255 \r
256 int dialogItems[][42] = {\r
257 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
258 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
259   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
260 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
261   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
262 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
263   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
264 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
265 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
266   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
267 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
268 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
269   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
270 { ABOUTBOX2, IDC_ChessBoard }, \r
271 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
272   OPT_GameListClose, IDC_GameListDoFilter }, \r
273 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
274 { DLG_Error, IDOK }, \r
275 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
276   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
277 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
278 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
279   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
280   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
281 { DLG_IndexNumber, IDC_Index }, \r
282 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
283 { DLG_TypeInName, IDOK, IDCANCEL }, \r
284 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
285   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
286 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
287   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
288   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
289   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
290   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
291   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
292   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
293 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
294   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
295   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
296   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
297   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
298   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
299   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
300   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
301   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
302 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
303   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
304   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
305   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
306   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
307   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
308   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
309   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
310 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
311   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
312   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
313   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
314   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
315   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
316   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
317   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
318   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
319 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
320   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
321   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
322   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
323   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
324 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
325 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
326   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
327 { DLG_MoveHistory }, \r
328 { DLG_EvalGraph }, \r
329 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
330 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
331 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
332   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
333   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
334   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
335 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
336   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
337   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
338 { 0 }\r
339 };\r
340 \r
341 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
342 static int lastChecked;\r
343 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
344 extern int tinyLayout;\r
345 extern char * menuBarText[][10];\r
346 \r
347 void\r
348 LoadLanguageFile(char *name)\r
349 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
350     FILE *f;\r
351     int i=0, j=0, n=0, k;\r
352     char buf[MSG_SIZ];\r
353 \r
354     if(!name || name[0] == NULLCHAR) return;\r
355       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
356     appData.language = oldLanguage;\r
357     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
358     if((f = fopen(buf, "r")) == NULL) return;\r
359     while((k = fgetc(f)) != EOF) {\r
360         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
361         languageBuf[i] = k;\r
362         if(k == '\n') {\r
363             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
364                 char *p;\r
365                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
366                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
367                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
368                         english[j] = languageBuf + n + 1; *p = 0;\r
369                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
370 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
371                     }\r
372                 }\r
373             }\r
374             n = i + 1;\r
375         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
376             switch(k) {\r
377               case 'n': k = '\n'; break;\r
378               case 'r': k = '\r'; break;\r
379               case 't': k = '\t'; break;\r
380             }\r
381             languageBuf[--i] = k;\r
382         }\r
383         i++;\r
384     }\r
385     fclose(f);\r
386     barbaric = (j != 0);\r
387     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
388 }\r
389 \r
390 char *\r
391 T_(char *s)\r
392 {   // return the translation of the given string\r
393     // efficiency can be improved a lot...\r
394     int i=0;\r
395     static char buf[MSG_SIZ];\r
396 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
397     if(!barbaric) return s;\r
398     if(!s) return ""; // sanity\r
399     while(english[i]) {\r
400         if(!strcmp(s, english[i])) return foreign[i];\r
401         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
402             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
403             return buf;\r
404         }\r
405         i++;\r
406     }\r
407     return s;\r
408 }\r
409 \r
410 void\r
411 Translate(HWND hDlg, int dialogID)\r
412 {   // translate all text items in the given dialog\r
413     int i=0, j, k;\r
414     char buf[MSG_SIZ], *s;\r
415     if(!barbaric) return;\r
416     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
417     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
418     GetWindowText( hDlg, buf, MSG_SIZ );\r
419     s = T_(buf);\r
420     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
421     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
422         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
423         if(strlen(buf) == 0) continue;\r
424         s = T_(buf);\r
425         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
426     }\r
427 }\r
428 \r
429 HMENU\r
430 TranslateOneMenu(int i, HMENU subMenu)\r
431 {\r
432     int j;\r
433     static MENUITEMINFO info;\r
434 \r
435     info.cbSize = sizeof(MENUITEMINFO);\r
436     info.fMask = MIIM_STATE | MIIM_TYPE;\r
437           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
438             char buf[MSG_SIZ];\r
439             info.dwTypeData = buf;\r
440             info.cch = sizeof(buf);\r
441             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
442             if(i < 10) {\r
443                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
444                 else menuText[i][j] = strdup(buf); // remember original on first change\r
445             }\r
446             if(buf[0] == NULLCHAR) continue;\r
447             info.dwTypeData = T_(buf);\r
448             info.cch = strlen(buf)+1;\r
449             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
450           }\r
451     return subMenu;\r
452 }\r
453 \r
454 void\r
455 TranslateMenus(int addLanguage)\r
456 {\r
457     int i;\r
458     WIN32_FIND_DATA fileData;\r
459     HANDLE hFind;\r
460 #define IDM_English 1970\r
461     if(1) {\r
462         HMENU mainMenu = GetMenu(hwndMain);\r
463         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
464           HMENU subMenu = GetSubMenu(mainMenu, i);\r
465           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
466                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
467           TranslateOneMenu(i, subMenu);\r
468         }\r
469         DrawMenuBar(hwndMain);\r
470     }\r
471 \r
472     if(!addLanguage) return;\r
473     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
474         HMENU mainMenu = GetMenu(hwndMain);\r
475         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
476         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
477         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
478         i = 0; lastChecked = IDM_English;\r
479         do {\r
480             char *p, *q = fileData.cFileName;\r
481             int checkFlag = MF_UNCHECKED;\r
482             languageFile[i] = strdup(q);\r
483             if(barbaric && !strcmp(oldLanguage, q)) {\r
484                 checkFlag = MF_CHECKED;\r
485                 lastChecked = IDM_English + i + 1;\r
486                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
487             }\r
488             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
489             p = strstr(fileData.cFileName, ".lng");\r
490             if(p) *p = 0;\r
491             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
492         } while(FindNextFile(hFind, &fileData));\r
493         FindClose(hFind);\r
494     }\r
495 }\r
496 \r
497 #endif\r
498 \r
499 #define IDM_RecentEngines 3000\r
500 \r
501 void\r
502 RecentEngineMenu (char *s)\r
503 {\r
504     if(appData.icsActive) return;\r
505     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
506         HMENU mainMenu = GetMenu(hwndMain);\r
507         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
508         int i=IDM_RecentEngines;\r
509         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
510         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
511         while(*s) {\r
512           char *p = strchr(s, '\n');\r
513           if(p == NULL) return; // malformed!\r
514           *p = NULLCHAR;\r
515           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
516           *p = '\n';\r
517           s = p+1;\r
518         }\r
519     }\r
520 }\r
521 \r
522 \r
523 typedef struct {\r
524   char *name;\r
525   int squareSize;\r
526   int lineGap;\r
527   int smallLayout;\r
528   int tinyLayout;\r
529   int cliWidth, cliHeight;\r
530 } SizeInfo;\r
531 \r
532 SizeInfo sizeInfo[] = \r
533 {\r
534   { "tiny",     21, 0, 1, 1, 0, 0 },\r
535   { "teeny",    25, 1, 1, 1, 0, 0 },\r
536   { "dinky",    29, 1, 1, 1, 0, 0 },\r
537   { "petite",   33, 1, 1, 1, 0, 0 },\r
538   { "slim",     37, 2, 1, 0, 0, 0 },\r
539   { "small",    40, 2, 1, 0, 0, 0 },\r
540   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
541   { "middling", 49, 2, 0, 0, 0, 0 },\r
542   { "average",  54, 2, 0, 0, 0, 0 },\r
543   { "moderate", 58, 3, 0, 0, 0, 0 },\r
544   { "medium",   64, 3, 0, 0, 0, 0 },\r
545   { "bulky",    72, 3, 0, 0, 0, 0 },\r
546   { "large",    80, 3, 0, 0, 0, 0 },\r
547   { "big",      87, 3, 0, 0, 0, 0 },\r
548   { "huge",     95, 3, 0, 0, 0, 0 },\r
549   { "giant",    108, 3, 0, 0, 0, 0 },\r
550   { "colossal", 116, 4, 0, 0, 0, 0 },\r
551   { "titanic",  129, 4, 0, 0, 0, 0 },\r
552   { NULL, 0, 0, 0, 0, 0, 0 }\r
553 };\r
554 \r
555 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
556 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
557 {\r
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576 };\r
577 \r
578 MyFont *font[NUM_SIZES][NUM_FONTS];\r
579 \r
580 typedef struct {\r
581   char *label;\r
582   int id;\r
583   HWND hwnd;\r
584   WNDPROC wndproc;\r
585 } MyButtonDesc;\r
586 \r
587 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
588 #define N_BUTTONS 5\r
589 \r
590 MyButtonDesc buttonDesc[N_BUTTONS] =\r
591 {\r
592   {"<<", IDM_ToStart, NULL, NULL},\r
593   {"<", IDM_Backward, NULL, NULL},\r
594   {"P", IDM_Pause, NULL, NULL},\r
595   {">", IDM_Forward, NULL, NULL},\r
596   {">>", IDM_ToEnd, NULL, NULL},\r
597 };\r
598 \r
599 int tinyLayout = 0, smallLayout = 0;\r
600 #define MENU_BAR_ITEMS 9\r
601 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
602   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
603   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
604 };\r
605 \r
606 \r
607 MySound sounds[(int)NSoundClasses];\r
608 MyTextAttribs textAttribs[(int)NColorClasses];\r
609 \r
610 MyColorizeAttribs colorizeAttribs[] = {\r
611   { (COLORREF)0, 0, N_("Shout Text") },\r
612   { (COLORREF)0, 0, N_("SShout/CShout") },\r
613   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
614   { (COLORREF)0, 0, N_("Channel Text") },\r
615   { (COLORREF)0, 0, N_("Kibitz Text") },\r
616   { (COLORREF)0, 0, N_("Tell Text") },\r
617   { (COLORREF)0, 0, N_("Challenge Text") },\r
618   { (COLORREF)0, 0, N_("Request Text") },\r
619   { (COLORREF)0, 0, N_("Seek Text") },\r
620   { (COLORREF)0, 0, N_("Normal Text") },\r
621   { (COLORREF)0, 0, N_("None") }\r
622 };\r
623 \r
624 \r
625 \r
626 static char *commentTitle;\r
627 static char *commentText;\r
628 static int commentIndex;\r
629 static Boolean editComment = FALSE;\r
630 \r
631 \r
632 char errorTitle[MSG_SIZ];\r
633 char errorMessage[2*MSG_SIZ];\r
634 HWND errorDialog = NULL;\r
635 BOOLEAN moveErrorMessageUp = FALSE;\r
636 BOOLEAN consoleEcho = TRUE;\r
637 CHARFORMAT consoleCF;\r
638 COLORREF consoleBackgroundColor;\r
639 \r
640 char *programVersion;\r
641 \r
642 #define CPReal 1\r
643 #define CPComm 2\r
644 #define CPSock 3\r
645 #define CPRcmd 4\r
646 typedef int CPKind;\r
647 \r
648 typedef struct {\r
649   CPKind kind;\r
650   HANDLE hProcess;\r
651   DWORD pid;\r
652   HANDLE hTo;\r
653   HANDLE hFrom;\r
654   SOCKET sock;\r
655   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
656 } ChildProc;\r
657 \r
658 #define INPUT_SOURCE_BUF_SIZE 4096\r
659 \r
660 typedef struct _InputSource {\r
661   CPKind kind;\r
662   HANDLE hFile;\r
663   SOCKET sock;\r
664   int lineByLine;\r
665   HANDLE hThread;\r
666   DWORD id;\r
667   char buf[INPUT_SOURCE_BUF_SIZE];\r
668   char *next;\r
669   DWORD count;\r
670   int error;\r
671   InputCallback func;\r
672   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
673   VOIDSTAR closure;\r
674 } InputSource;\r
675 \r
676 InputSource *consoleInputSource;\r
677 \r
678 DCB dcb;\r
679 \r
680 /* forward */\r
681 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
682 VOID ConsoleCreate();\r
683 LRESULT CALLBACK\r
684   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
685 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
686 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
687 VOID ParseCommSettings(char *arg, DCB *dcb);\r
688 LRESULT CALLBACK\r
689   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
690 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
691 void ParseIcsTextMenu(char *icsTextMenuString);\r
692 VOID PopUpNameDialog(char firstchar);\r
693 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
694 \r
695 /* [AS] */\r
696 int NewGameFRC();\r
697 int GameListOptions();\r
698 \r
699 int dummy; // [HGM] for obsolete args\r
700 \r
701 HWND hwndMain = NULL;        /* root window*/\r
702 HWND hwndConsole = NULL;\r
703 HWND commentDialog = NULL;\r
704 HWND moveHistoryDialog = NULL;\r
705 HWND evalGraphDialog = NULL;\r
706 HWND engineOutputDialog = NULL;\r
707 HWND gameListDialog = NULL;\r
708 HWND editTagsDialog = NULL;\r
709 \r
710 int commentUp = FALSE;\r
711 \r
712 WindowPlacement wpMain;\r
713 WindowPlacement wpConsole;\r
714 WindowPlacement wpComment;\r
715 WindowPlacement wpMoveHistory;\r
716 WindowPlacement wpEvalGraph;\r
717 WindowPlacement wpEngineOutput;\r
718 WindowPlacement wpGameList;\r
719 WindowPlacement wpTags;\r
720 \r
721 VOID EngineOptionsPopup(); // [HGM] settings\r
722 \r
723 VOID GothicPopUp(char *title, VariantClass variant);\r
724 /*\r
725  * Setting "frozen" should disable all user input other than deleting\r
726  * the window.  We do this while engines are initializing themselves.\r
727  */\r
728 static int frozen = 0;\r
729 static int oldMenuItemState[MENU_BAR_ITEMS];\r
730 void FreezeUI()\r
731 {\r
732   HMENU hmenu;\r
733   int i;\r
734 \r
735   if (frozen) return;\r
736   frozen = 1;\r
737   hmenu = GetMenu(hwndMain);\r
738   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
739     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
740   }\r
741   DrawMenuBar(hwndMain);\r
742 }\r
743 \r
744 /* Undo a FreezeUI */\r
745 void ThawUI()\r
746 {\r
747   HMENU hmenu;\r
748   int i;\r
749 \r
750   if (!frozen) return;\r
751   frozen = 0;\r
752   hmenu = GetMenu(hwndMain);\r
753   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
754     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
755   }\r
756   DrawMenuBar(hwndMain);\r
757 }\r
758 \r
759 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
760 \r
761 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
762 #ifdef JAWS\r
763 #include "jaws.c"\r
764 #else\r
765 #define JAWS_INIT\r
766 #define JAWS_ARGS\r
767 #define JAWS_ALT_INTERCEPT\r
768 #define JAWS_KBUP_NAVIGATION\r
769 #define JAWS_KBDOWN_NAVIGATION\r
770 #define JAWS_MENU_ITEMS\r
771 #define JAWS_SILENCE\r
772 #define JAWS_REPLAY\r
773 #define JAWS_ACCEL\r
774 #define JAWS_COPYRIGHT\r
775 #define JAWS_DELETE(X) X\r
776 #define SAYMACHINEMOVE()\r
777 #define SAY(X)\r
778 #endif\r
779 \r
780 /*---------------------------------------------------------------------------*\\r
781  *\r
782  * WinMain\r
783  *\r
784 \*---------------------------------------------------------------------------*/\r
785 \r
786 int APIENTRY\r
787 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
788         LPSTR lpCmdLine, int nCmdShow)\r
789 {\r
790   MSG msg;\r
791   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 //  INITCOMMONCONTROLSEX ex;\r
793 \r
794   debugFP = stderr;\r
795 \r
796   LoadLibrary("RICHED32.DLL");\r
797   consoleCF.cbSize = sizeof(CHARFORMAT);\r
798 \r
799   if (!InitApplication(hInstance)) {\r
800     return (FALSE);\r
801   }\r
802   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
803     return (FALSE);\r
804   }\r
805 \r
806   JAWS_INIT\r
807   TranslateMenus(1);\r
808 \r
809 //  InitCommonControlsEx(&ex);\r
810   InitCommonControls();\r
811 \r
812   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
813   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
814   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
815 \r
816   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
817 \r
818   while (GetMessage(&msg, /* message structure */\r
819                     NULL, /* handle of window receiving the message */\r
820                     0,    /* lowest message to examine */\r
821                     0))   /* highest message to examine */\r
822     {\r
823 \r
824       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
825         // [HGM] navigate: switch between all windows with tab\r
826         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
827         int i, currentElement = 0;\r
828 \r
829         // first determine what element of the chain we come from (if any)\r
830         if(appData.icsActive) {\r
831             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
832             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
833         }\r
834         if(engineOutputDialog && EngineOutputIsUp()) {\r
835             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
836             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
837         }\r
838         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
839             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
840         }\r
841         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
842         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
843         if(msg.hwnd == e1)                 currentElement = 2; else\r
844         if(msg.hwnd == e2)                 currentElement = 3; else\r
845         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
846         if(msg.hwnd == mh)                currentElement = 4; else\r
847         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
848         if(msg.hwnd == hText)  currentElement = 5; else\r
849         if(msg.hwnd == hInput) currentElement = 6; else\r
850         for (i = 0; i < N_BUTTONS; i++) {\r
851             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
852         }\r
853 \r
854         // determine where to go to\r
855         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
856           do {\r
857             currentElement = (currentElement + direction) % 7;\r
858             switch(currentElement) {\r
859                 case 0:\r
860                   h = hwndMain; break; // passing this case always makes the loop exit\r
861                 case 1:\r
862                   h = buttonDesc[0].hwnd; break; // could be NULL\r
863                 case 2:\r
864                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
865                   h = e1; break;\r
866                 case 3:\r
867                   if(!EngineOutputIsUp()) continue;\r
868                   h = e2; break;\r
869                 case 4:\r
870                   if(!MoveHistoryIsUp()) continue;\r
871                   h = mh; break;\r
872 //              case 6: // input to eval graph does not seem to get here!\r
873 //                if(!EvalGraphIsUp()) continue;\r
874 //                h = evalGraphDialog; break;\r
875                 case 5:\r
876                   if(!appData.icsActive) continue;\r
877                   SAY("display");\r
878                   h = hText; break;\r
879                 case 6:\r
880                   if(!appData.icsActive) continue;\r
881                   SAY("input");\r
882                   h = hInput; break;\r
883             }\r
884           } while(h == 0);\r
885 \r
886           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
887           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
888           SetFocus(h);\r
889 \r
890           continue; // this message now has been processed\r
891         }\r
892       }\r
893 \r
894       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
895           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
896           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
897           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
898           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
899           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
900           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
901           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
902           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
903           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
904         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
905         for(i=0; i<MAX_CHAT; i++) \r
906             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
907                 done = 1; break;\r
908         }\r
909         if(done) continue; // [HGM] chat: end patch\r
910         TranslateMessage(&msg); /* Translates virtual key codes */\r
911         DispatchMessage(&msg);  /* Dispatches message to window */\r
912       }\r
913     }\r
914 \r
915 \r
916   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
917 }\r
918 \r
919 /*---------------------------------------------------------------------------*\\r
920  *\r
921  * Initialization functions\r
922  *\r
923 \*---------------------------------------------------------------------------*/\r
924 \r
925 void\r
926 SetUserLogo()\r
927 {   // update user logo if necessary\r
928     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
929 \r
930     if(appData.autoLogo) {\r
931           curName = UserName();\r
932           if(strcmp(curName, oldUserName)) {\r
933                 GetCurrentDirectory(MSG_SIZ, dir);\r
934                 SetCurrentDirectory(installDir);\r
935                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
936                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
937                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
938                 if(userLogo == NULL)\r
939                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
940                 SetCurrentDirectory(dir); /* return to prev directory */\r
941           }\r
942     }\r
943 }\r
944 \r
945 BOOL\r
946 InitApplication(HINSTANCE hInstance)\r
947 {\r
948   WNDCLASS wc;\r
949 \r
950   /* Fill in window class structure with parameters that describe the */\r
951   /* main window. */\r
952 \r
953   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
954   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
955   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
956   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
957   wc.hInstance     = hInstance;         /* Owner of this class */\r
958   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
959   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
960   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
961   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
962   wc.lpszClassName = szAppName;                 /* Name to register as */\r
963 \r
964   /* Register the window class and return success/failure code. */\r
965   if (!RegisterClass(&wc)) return FALSE;\r
966 \r
967   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
968   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
969   wc.cbClsExtra    = 0;\r
970   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
971   wc.hInstance     = hInstance;\r
972   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
973   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
974   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
975   wc.lpszMenuName  = NULL;\r
976   wc.lpszClassName = szConsoleName;\r
977 \r
978   if (!RegisterClass(&wc)) return FALSE;\r
979   return TRUE;\r
980 }\r
981 \r
982 \r
983 /* Set by InitInstance, used by EnsureOnScreen */\r
984 int screenHeight, screenWidth;\r
985 \r
986 void\r
987 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
988 {\r
989 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
990   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
991   if (*x > screenWidth - 32) *x = 0;\r
992   if (*y > screenHeight - 32) *y = 0;\r
993   if (*x < minX) *x = minX;\r
994   if (*y < minY) *y = minY;\r
995 }\r
996 \r
997 VOID\r
998 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
999 {\r
1000   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1001   GetCurrentDirectory(MSG_SIZ, dir);\r
1002   SetCurrentDirectory(installDir);\r
1003   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1004       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1005 \r
1006       if (cps->programLogo == NULL && appData.debugMode) {\r
1007           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1008       }\r
1009   } else if(appData.autoLogo) {\r
1010       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1011         char *opponent = "";\r
1012         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1013         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1014         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1015         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1016             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1017             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1018         }\r
1019       } else\r
1020       if(appData.directory[n] && appData.directory[n][0]) {\r
1021         SetCurrentDirectory(appData.directory[n]);\r
1022         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1023       }\r
1024   }\r
1025   SetCurrentDirectory(dir); /* return to prev directory */\r
1026 }\r
1027 \r
1028 VOID\r
1029 InitTextures()\r
1030 {\r
1031   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1032   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1033   \r
1034   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1035       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1036       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       liteBackTextureMode = appData.liteBackTextureMode;\r
1038 \r
1039       if (liteBackTexture == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1041       }\r
1042   }\r
1043   \r
1044   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1045       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1046       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1047       darkBackTextureMode = appData.darkBackTextureMode;\r
1048 \r
1049       if (darkBackTexture == NULL && appData.debugMode) {\r
1050           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1051       }\r
1052   }\r
1053 }\r
1054 \r
1055 BOOL\r
1056 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1057 {\r
1058   HWND hwnd; /* Main window handle. */\r
1059   int ibs;\r
1060   WINDOWPLACEMENT wp;\r
1061   char *filepart;\r
1062 \r
1063   hInst = hInstance;    /* Store instance handle in our global variable */\r
1064   programName = szAppName;\r
1065 \r
1066   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1067     *filepart = NULLCHAR;\r
1068     SetCurrentDirectory(installDir);\r
1069   } else {\r
1070     GetCurrentDirectory(MSG_SIZ, installDir);\r
1071   }\r
1072   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1073   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1074   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1075   /* xboard, and older WinBoards, controlled the move sound with the\r
1076      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1077      always turn the option on (so that the backend will call us),\r
1078      then let the user turn the sound off by setting it to silence if\r
1079      desired.  To accommodate old winboard.ini files saved by old\r
1080      versions of WinBoard, we also turn off the sound if the option\r
1081      was initially set to false. [HGM] taken out of InitAppData */\r
1082   if (!appData.ringBellAfterMoves) {\r
1083     sounds[(int)SoundMove].name = strdup("");\r
1084     appData.ringBellAfterMoves = TRUE;\r
1085   }\r
1086   if (appData.debugMode) {\r
1087     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1088     setbuf(debugFP, NULL);\r
1089   }\r
1090 \r
1091   LoadLanguageFile(appData.language);\r
1092 \r
1093   InitBackEnd1();\r
1094 \r
1095 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1096 //  InitEngineUCI( installDir, &second );\r
1097 \r
1098   /* Create a main window for this application instance. */\r
1099   hwnd = CreateWindow(szAppName, szTitle,\r
1100                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1101                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1102                       NULL, NULL, hInstance, NULL);\r
1103   hwndMain = hwnd;\r
1104 \r
1105   /* If window could not be created, return "failure" */\r
1106   if (!hwnd) {\r
1107     return (FALSE);\r
1108   }\r
1109 \r
1110   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1111   LoadLogo(&first, 0, FALSE);\r
1112   LoadLogo(&second, 1, appData.icsActive);\r
1113 \r
1114   SetUserLogo();\r
1115 \r
1116   iconWhite = LoadIcon(hInstance, "icon_white");\r
1117   iconBlack = LoadIcon(hInstance, "icon_black");\r
1118   iconCurrent = iconWhite;\r
1119   InitDrawingColors();\r
1120   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1121   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1122   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1123   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1124     /* Compute window size for each board size, and use the largest\r
1125        size that fits on this screen as the default. */\r
1126     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1127     if (boardSize == (BoardSize)-1 &&\r
1128         winH <= screenHeight\r
1129            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1130         && winW <= screenWidth) {\r
1131       boardSize = (BoardSize)ibs;\r
1132     }\r
1133   }\r
1134 \r
1135   InitDrawingSizes(boardSize, 0);\r
1136   RecentEngineMenu(appData.recentEngineList);\r
1137   InitMenuChecks();\r
1138   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1139 \r
1140   /* [AS] Load textures if specified */\r
1141   InitTextures();\r
1142 \r
1143   mysrandom( (unsigned) time(NULL) );\r
1144 \r
1145   /* [AS] Restore layout */\r
1146   if( wpMoveHistory.visible ) {\r
1147       MoveHistoryPopUp();\r
1148   }\r
1149 \r
1150   if( wpEvalGraph.visible ) {\r
1151       EvalGraphPopUp();\r
1152   }\r
1153 \r
1154   if( wpEngineOutput.visible ) {\r
1155       EngineOutputPopUp();\r
1156   }\r
1157 \r
1158   /* Make the window visible; update its client area; and return "success" */\r
1159   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1160   wp.length = sizeof(WINDOWPLACEMENT);\r
1161   wp.flags = 0;\r
1162   wp.showCmd = nCmdShow;\r
1163   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1164   wp.rcNormalPosition.left = wpMain.x;\r
1165   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1166   wp.rcNormalPosition.top = wpMain.y;\r
1167   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1168   SetWindowPlacement(hwndMain, &wp);\r
1169 \r
1170   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1171 \r
1172   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1173                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1174 \r
1175   if (hwndConsole) {\r
1176 #if AOT_CONSOLE\r
1177     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1178                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1179 #endif\r
1180     ShowWindow(hwndConsole, nCmdShow);\r
1181     SetActiveWindow(hwndConsole);\r
1182   }\r
1183   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1184   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1185 \r
1186   return TRUE;\r
1187 \r
1188 }\r
1189 \r
1190 VOID\r
1191 InitMenuChecks()\r
1192 {\r
1193   HMENU hmenu = GetMenu(hwndMain);\r
1194 \r
1195   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1196                         MF_BYCOMMAND|((appData.icsActive &&\r
1197                                        *appData.icsCommPort != NULLCHAR) ?\r
1198                                       MF_ENABLED : MF_GRAYED));\r
1199   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1200                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1201                                      MF_CHECKED : MF_UNCHECKED));\r
1202 }\r
1203 \r
1204 //---------------------------------------------------------------------------------------------------------\r
1205 \r
1206 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1207 #define XBOARD FALSE\r
1208 \r
1209 #define OPTCHAR "/"\r
1210 #define SEPCHAR "="\r
1211 #define TOPLEVEL 0\r
1212 \r
1213 #include "args.h"\r
1214 \r
1215 // front-end part of option handling\r
1216 \r
1217 VOID\r
1218 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1219 {\r
1220   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1221   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1222   DeleteDC(hdc);\r
1223   lf->lfWidth = 0;\r
1224   lf->lfEscapement = 0;\r
1225   lf->lfOrientation = 0;\r
1226   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1227   lf->lfItalic = mfp->italic;\r
1228   lf->lfUnderline = mfp->underline;\r
1229   lf->lfStrikeOut = mfp->strikeout;\r
1230   lf->lfCharSet = mfp->charset;\r
1231   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1232   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1233   lf->lfQuality = DEFAULT_QUALITY;\r
1234   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1235     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1236 }\r
1237 \r
1238 void\r
1239 CreateFontInMF(MyFont *mf)\r
1240\r
1241   LFfromMFP(&mf->lf, &mf->mfp);\r
1242   if (mf->hf) DeleteObject(mf->hf);\r
1243   mf->hf = CreateFontIndirect(&mf->lf);\r
1244 }\r
1245 \r
1246 // [HGM] This platform-dependent table provides the location for storing the color info\r
1247 void *\r
1248 colorVariable[] = {\r
1249   &whitePieceColor, \r
1250   &blackPieceColor, \r
1251   &lightSquareColor,\r
1252   &darkSquareColor, \r
1253   &highlightSquareColor,\r
1254   &premoveHighlightColor,\r
1255   NULL,\r
1256   &consoleBackgroundColor,\r
1257   &appData.fontForeColorWhite,\r
1258   &appData.fontBackColorWhite,\r
1259   &appData.fontForeColorBlack,\r
1260   &appData.fontBackColorBlack,\r
1261   &appData.evalHistColorWhite,\r
1262   &appData.evalHistColorBlack,\r
1263   &appData.highlightArrowColor,\r
1264 };\r
1265 \r
1266 /* Command line font name parser.  NULL name means do nothing.\r
1267    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1268    For backward compatibility, syntax without the colon is also\r
1269    accepted, but font names with digits in them won't work in that case.\r
1270 */\r
1271 VOID\r
1272 ParseFontName(char *name, MyFontParams *mfp)\r
1273 {\r
1274   char *p, *q;\r
1275   if (name == NULL) return;\r
1276   p = name;\r
1277   q = strchr(p, ':');\r
1278   if (q) {\r
1279     if (q - p >= sizeof(mfp->faceName))\r
1280       ExitArgError(_("Font name too long:"), name, TRUE);\r
1281     memcpy(mfp->faceName, p, q - p);\r
1282     mfp->faceName[q - p] = NULLCHAR;\r
1283     p = q + 1;\r
1284   } else {\r
1285     q = mfp->faceName;\r
1286 \r
1287     while (*p && !isdigit(*p)) {\r
1288       *q++ = *p++;\r
1289       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1290         ExitArgError(_("Font name too long:"), name, TRUE);\r
1291     }\r
1292     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1293     *q = NULLCHAR;\r
1294   }\r
1295   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1296   mfp->pointSize = (float) atof(p);\r
1297   mfp->bold = (strchr(p, 'b') != NULL);\r
1298   mfp->italic = (strchr(p, 'i') != NULL);\r
1299   mfp->underline = (strchr(p, 'u') != NULL);\r
1300   mfp->strikeout = (strchr(p, 's') != NULL);\r
1301   mfp->charset = DEFAULT_CHARSET;\r
1302   q = strchr(p, 'c');\r
1303   if (q)\r
1304     mfp->charset = (BYTE) atoi(q+1);\r
1305 }\r
1306 \r
1307 void\r
1308 ParseFont(char *name, int number)\r
1309 { // wrapper to shield back-end from 'font'\r
1310   ParseFontName(name, &font[boardSize][number]->mfp);\r
1311 }\r
1312 \r
1313 void\r
1314 SetFontDefaults()\r
1315 { // in WB  we have a 2D array of fonts; this initializes their description\r
1316   int i, j;\r
1317   /* Point font array elements to structures and\r
1318      parse default font names */\r
1319   for (i=0; i<NUM_FONTS; i++) {\r
1320     for (j=0; j<NUM_SIZES; j++) {\r
1321       font[j][i] = &fontRec[j][i];\r
1322       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1323     }\r
1324   }\r
1325 }\r
1326 \r
1327 void\r
1328 CreateFonts()\r
1329 { // here we create the actual fonts from the selected descriptions\r
1330   int i, j;\r
1331   for (i=0; i<NUM_FONTS; i++) {\r
1332     for (j=0; j<NUM_SIZES; j++) {\r
1333       CreateFontInMF(font[j][i]);\r
1334     }\r
1335   }\r
1336 }\r
1337 /* Color name parser.\r
1338    X version accepts X color names, but this one\r
1339    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1340 COLORREF\r
1341 ParseColorName(char *name)\r
1342 {\r
1343   int red, green, blue, count;\r
1344   char buf[MSG_SIZ];\r
1345 \r
1346   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1347   if (count != 3) {\r
1348     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1349       &red, &green, &blue);\r
1350   }\r
1351   if (count != 3) {\r
1352     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1353     DisplayError(buf, 0);\r
1354     return RGB(0, 0, 0);\r
1355   }\r
1356   return PALETTERGB(red, green, blue);\r
1357 }\r
1358 \r
1359 void\r
1360 ParseColor(int n, char *name)\r
1361 { // for WinBoard the color is an int, which needs to be derived from the string\r
1362   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1363 }\r
1364 \r
1365 void\r
1366 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1367 {\r
1368   char *e = argValue;\r
1369   int eff = 0;\r
1370 \r
1371   while (*e) {\r
1372     if (*e == 'b')      eff |= CFE_BOLD;\r
1373     else if (*e == 'i') eff |= CFE_ITALIC;\r
1374     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1375     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1376     else if (*e == '#' || isdigit(*e)) break;\r
1377     e++;\r
1378   }\r
1379   *effects = eff;\r
1380   *color   = ParseColorName(e);\r
1381 }\r
1382 \r
1383 void\r
1384 ParseTextAttribs(ColorClass cc, char *s)\r
1385 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1386     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1387     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1388 }\r
1389 \r
1390 void\r
1391 ParseBoardSize(void *addr, char *name)\r
1392 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1393   BoardSize bs = SizeTiny;\r
1394   while (sizeInfo[bs].name != NULL) {\r
1395     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1396         *(BoardSize *)addr = bs;\r
1397         return;\r
1398     }\r
1399     bs++;\r
1400   }\r
1401   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1402 }\r
1403 \r
1404 void\r
1405 LoadAllSounds()\r
1406 { // [HGM] import name from appData first\r
1407   ColorClass cc;\r
1408   SoundClass sc;\r
1409   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1410     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1411     textAttribs[cc].sound.data = NULL;\r
1412     MyLoadSound(&textAttribs[cc].sound);\r
1413   }\r
1414   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1415     textAttribs[cc].sound.name = strdup("");\r
1416     textAttribs[cc].sound.data = NULL;\r
1417   }\r
1418   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1419     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1420     sounds[sc].data = NULL;\r
1421     MyLoadSound(&sounds[sc]);\r
1422   }\r
1423 }\r
1424 \r
1425 void\r
1426 SetCommPortDefaults()\r
1427 {\r
1428    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1429   dcb.DCBlength = sizeof(DCB);\r
1430   dcb.BaudRate = 9600;\r
1431   dcb.fBinary = TRUE;\r
1432   dcb.fParity = FALSE;\r
1433   dcb.fOutxCtsFlow = FALSE;\r
1434   dcb.fOutxDsrFlow = FALSE;\r
1435   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1436   dcb.fDsrSensitivity = FALSE;\r
1437   dcb.fTXContinueOnXoff = TRUE;\r
1438   dcb.fOutX = FALSE;\r
1439   dcb.fInX = FALSE;\r
1440   dcb.fNull = FALSE;\r
1441   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1442   dcb.fAbortOnError = FALSE;\r
1443   dcb.ByteSize = 7;\r
1444   dcb.Parity = SPACEPARITY;\r
1445   dcb.StopBits = ONESTOPBIT;\r
1446 }\r
1447 \r
1448 // [HGM] args: these three cases taken out to stay in front-end\r
1449 void\r
1450 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1451 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1452         // while the curent board size determines the element. This system should be ported to XBoard.\r
1453         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1454         int bs;\r
1455         for (bs=0; bs<NUM_SIZES; bs++) {\r
1456           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1457           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1458           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1459             ad->argName, mfp->faceName, mfp->pointSize,\r
1460             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1461             mfp->bold ? "b" : "",\r
1462             mfp->italic ? "i" : "",\r
1463             mfp->underline ? "u" : "",\r
1464             mfp->strikeout ? "s" : "",\r
1465             (int)mfp->charset);\r
1466         }\r
1467       }\r
1468 \r
1469 void\r
1470 ExportSounds()\r
1471 { // [HGM] copy the names from the internal WB variables to appData\r
1472   ColorClass cc;\r
1473   SoundClass sc;\r
1474   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1475     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1476   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1477     (&appData.soundMove)[sc] = sounds[sc].name;\r
1478 }\r
1479 \r
1480 void\r
1481 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1482 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1483         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1484         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1485           (ta->effects & CFE_BOLD) ? "b" : "",\r
1486           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1487           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1488           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1489           (ta->effects) ? " " : "",\r
1490           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1491       }\r
1492 \r
1493 void\r
1494 SaveColor(FILE *f, ArgDescriptor *ad)\r
1495 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1496         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1497         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1498           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1499 }\r
1500 \r
1501 void\r
1502 SaveBoardSize(FILE *f, char *name, void *addr)\r
1503 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1504   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1505 }\r
1506 \r
1507 void\r
1508 ParseCommPortSettings(char *s)\r
1509 { // wrapper to keep dcb from back-end\r
1510   ParseCommSettings(s, &dcb);\r
1511 }\r
1512 \r
1513 void\r
1514 GetWindowCoords()\r
1515 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1516   GetActualPlacement(hwndMain, &wpMain);\r
1517   GetActualPlacement(hwndConsole, &wpConsole);\r
1518   GetActualPlacement(commentDialog, &wpComment);\r
1519   GetActualPlacement(editTagsDialog, &wpTags);\r
1520   GetActualPlacement(gameListDialog, &wpGameList);\r
1521   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1522   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1523   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1524 }\r
1525 \r
1526 void\r
1527 PrintCommPortSettings(FILE *f, char *name)\r
1528 { // wrapper to shield back-end from DCB\r
1529       PrintCommSettings(f, name, &dcb);\r
1530 }\r
1531 \r
1532 int\r
1533 MySearchPath(char *installDir, char *name, char *fullname)\r
1534 {\r
1535   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1536   if(name[0]== '%') {\r
1537     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1538     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1539       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1540       *strchr(buf, '%') = 0;\r
1541       strcat(fullname, getenv(buf));\r
1542       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1543     }\r
1544     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1545     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1546     return (int) strlen(fullname);\r
1547   }\r
1548   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1549 }\r
1550 \r
1551 int\r
1552 MyGetFullPathName(char *name, char *fullname)\r
1553 {\r
1554   char *dummy;\r
1555   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1556 }\r
1557 \r
1558 int\r
1559 MainWindowUp()\r
1560 { // [HGM] args: allows testing if main window is realized from back-end\r
1561   return hwndMain != NULL;\r
1562 }\r
1563 \r
1564 void\r
1565 PopUpStartupDialog()\r
1566 {\r
1567     FARPROC lpProc;\r
1568     \r
1569     LoadLanguageFile(appData.language);\r
1570     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1571     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1572     FreeProcInstance(lpProc);\r
1573 }\r
1574 \r
1575 /*---------------------------------------------------------------------------*\\r
1576  *\r
1577  * GDI board drawing routines\r
1578  *\r
1579 \*---------------------------------------------------------------------------*/\r
1580 \r
1581 /* [AS] Draw square using background texture */\r
1582 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1583 {\r
1584     XFORM   x;\r
1585 \r
1586     if( mode == 0 ) {\r
1587         return; /* Should never happen! */\r
1588     }\r
1589 \r
1590     SetGraphicsMode( dst, GM_ADVANCED );\r
1591 \r
1592     switch( mode ) {\r
1593     case 1:\r
1594         /* Identity */\r
1595         break;\r
1596     case 2:\r
1597         /* X reflection */\r
1598         x.eM11 = -1.0;\r
1599         x.eM12 = 0;\r
1600         x.eM21 = 0;\r
1601         x.eM22 = 1.0;\r
1602         x.eDx = (FLOAT) dw + dx - 1;\r
1603         x.eDy = 0;\r
1604         dx = 0;\r
1605         SetWorldTransform( dst, &x );\r
1606         break;\r
1607     case 3:\r
1608         /* Y reflection */\r
1609         x.eM11 = 1.0;\r
1610         x.eM12 = 0;\r
1611         x.eM21 = 0;\r
1612         x.eM22 = -1.0;\r
1613         x.eDx = 0;\r
1614         x.eDy = (FLOAT) dh + dy - 1;\r
1615         dy = 0;\r
1616         SetWorldTransform( dst, &x );\r
1617         break;\r
1618     case 4:\r
1619         /* X/Y flip */\r
1620         x.eM11 = 0;\r
1621         x.eM12 = 1.0;\r
1622         x.eM21 = 1.0;\r
1623         x.eM22 = 0;\r
1624         x.eDx = (FLOAT) dx;\r
1625         x.eDy = (FLOAT) dy;\r
1626         dx = 0;\r
1627         dy = 0;\r
1628         SetWorldTransform( dst, &x );\r
1629         break;\r
1630     }\r
1631 \r
1632     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1633 \r
1634     x.eM11 = 1.0;\r
1635     x.eM12 = 0;\r
1636     x.eM21 = 0;\r
1637     x.eM22 = 1.0;\r
1638     x.eDx = 0;\r
1639     x.eDy = 0;\r
1640     SetWorldTransform( dst, &x );\r
1641 \r
1642     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1643 }\r
1644 \r
1645 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1646 enum {\r
1647     PM_WP = (int) WhitePawn, \r
1648     PM_WN = (int) WhiteKnight, \r
1649     PM_WB = (int) WhiteBishop, \r
1650     PM_WR = (int) WhiteRook, \r
1651     PM_WQ = (int) WhiteQueen, \r
1652     PM_WF = (int) WhiteFerz, \r
1653     PM_WW = (int) WhiteWazir, \r
1654     PM_WE = (int) WhiteAlfil, \r
1655     PM_WM = (int) WhiteMan, \r
1656     PM_WO = (int) WhiteCannon, \r
1657     PM_WU = (int) WhiteUnicorn, \r
1658     PM_WH = (int) WhiteNightrider, \r
1659     PM_WA = (int) WhiteAngel, \r
1660     PM_WC = (int) WhiteMarshall, \r
1661     PM_WAB = (int) WhiteCardinal, \r
1662     PM_WD = (int) WhiteDragon, \r
1663     PM_WL = (int) WhiteLance, \r
1664     PM_WS = (int) WhiteCobra, \r
1665     PM_WV = (int) WhiteFalcon, \r
1666     PM_WSG = (int) WhiteSilver, \r
1667     PM_WG = (int) WhiteGrasshopper, \r
1668     PM_WK = (int) WhiteKing,\r
1669     PM_BP = (int) BlackPawn, \r
1670     PM_BN = (int) BlackKnight, \r
1671     PM_BB = (int) BlackBishop, \r
1672     PM_BR = (int) BlackRook, \r
1673     PM_BQ = (int) BlackQueen, \r
1674     PM_BF = (int) BlackFerz, \r
1675     PM_BW = (int) BlackWazir, \r
1676     PM_BE = (int) BlackAlfil, \r
1677     PM_BM = (int) BlackMan,\r
1678     PM_BO = (int) BlackCannon, \r
1679     PM_BU = (int) BlackUnicorn, \r
1680     PM_BH = (int) BlackNightrider, \r
1681     PM_BA = (int) BlackAngel, \r
1682     PM_BC = (int) BlackMarshall, \r
1683     PM_BG = (int) BlackGrasshopper, \r
1684     PM_BAB = (int) BlackCardinal,\r
1685     PM_BD = (int) BlackDragon,\r
1686     PM_BL = (int) BlackLance,\r
1687     PM_BS = (int) BlackCobra,\r
1688     PM_BV = (int) BlackFalcon,\r
1689     PM_BSG = (int) BlackSilver,\r
1690     PM_BK = (int) BlackKing\r
1691 };\r
1692 \r
1693 static HFONT hPieceFont = NULL;\r
1694 static HBITMAP hPieceMask[(int) EmptySquare];\r
1695 static HBITMAP hPieceFace[(int) EmptySquare];\r
1696 static int fontBitmapSquareSize = 0;\r
1697 static char pieceToFontChar[(int) EmptySquare] =\r
1698                               { 'p', 'n', 'b', 'r', 'q', \r
1699                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1700                       'k', 'o', 'm', 'v', 't', 'w', \r
1701                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1702                                                               'l' };\r
1703 \r
1704 extern BOOL SetCharTable( char *table, const char * map );\r
1705 /* [HGM] moved to backend.c */\r
1706 \r
1707 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1708 {\r
1709     HBRUSH hbrush;\r
1710     BYTE r1 = GetRValue( color );\r
1711     BYTE g1 = GetGValue( color );\r
1712     BYTE b1 = GetBValue( color );\r
1713     BYTE r2 = r1 / 2;\r
1714     BYTE g2 = g1 / 2;\r
1715     BYTE b2 = b1 / 2;\r
1716     RECT rc;\r
1717 \r
1718     /* Create a uniform background first */\r
1719     hbrush = CreateSolidBrush( color );\r
1720     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1721     FillRect( hdc, &rc, hbrush );\r
1722     DeleteObject( hbrush );\r
1723     \r
1724     if( mode == 1 ) {\r
1725         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1726         int steps = squareSize / 2;\r
1727         int i;\r
1728 \r
1729         for( i=0; i<steps; i++ ) {\r
1730             BYTE r = r1 - (r1-r2) * i / steps;\r
1731             BYTE g = g1 - (g1-g2) * i / steps;\r
1732             BYTE b = b1 - (b1-b2) * i / steps;\r
1733 \r
1734             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1735             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1736             FillRect( hdc, &rc, hbrush );\r
1737             DeleteObject(hbrush);\r
1738         }\r
1739     }\r
1740     else if( mode == 2 ) {\r
1741         /* Diagonal gradient, good more or less for every piece */\r
1742         POINT triangle[3];\r
1743         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1744         HBRUSH hbrush_old;\r
1745         int steps = squareSize;\r
1746         int i;\r
1747 \r
1748         triangle[0].x = squareSize - steps;\r
1749         triangle[0].y = squareSize;\r
1750         triangle[1].x = squareSize;\r
1751         triangle[1].y = squareSize;\r
1752         triangle[2].x = squareSize;\r
1753         triangle[2].y = squareSize - steps;\r
1754 \r
1755         for( i=0; i<steps; i++ ) {\r
1756             BYTE r = r1 - (r1-r2) * i / steps;\r
1757             BYTE g = g1 - (g1-g2) * i / steps;\r
1758             BYTE b = b1 - (b1-b2) * i / steps;\r
1759 \r
1760             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1761             hbrush_old = SelectObject( hdc, hbrush );\r
1762             Polygon( hdc, triangle, 3 );\r
1763             SelectObject( hdc, hbrush_old );\r
1764             DeleteObject(hbrush);\r
1765             triangle[0].x++;\r
1766             triangle[2].y++;\r
1767         }\r
1768 \r
1769         SelectObject( hdc, hpen );\r
1770     }\r
1771 }\r
1772 \r
1773 /*\r
1774     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1775     seems to work ok. The main problem here is to find the "inside" of a chess\r
1776     piece: follow the steps as explained below.\r
1777 */\r
1778 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1779 {\r
1780     HBITMAP hbm;\r
1781     HBITMAP hbm_old;\r
1782     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1783     RECT rc;\r
1784     SIZE sz;\r
1785     POINT pt;\r
1786     int backColor = whitePieceColor; \r
1787     int foreColor = blackPieceColor;\r
1788     \r
1789     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1790         backColor = appData.fontBackColorWhite;\r
1791         foreColor = appData.fontForeColorWhite;\r
1792     }\r
1793     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1794         backColor = appData.fontBackColorBlack;\r
1795         foreColor = appData.fontForeColorBlack;\r
1796     }\r
1797 \r
1798     /* Mask */\r
1799     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1800 \r
1801     hbm_old = SelectObject( hdc, hbm );\r
1802 \r
1803     rc.left = 0;\r
1804     rc.top = 0;\r
1805     rc.right = squareSize;\r
1806     rc.bottom = squareSize;\r
1807 \r
1808     /* Step 1: background is now black */\r
1809     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1810 \r
1811     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1812 \r
1813     pt.x = (squareSize - sz.cx) / 2;\r
1814     pt.y = (squareSize - sz.cy) / 2;\r
1815 \r
1816     SetBkMode( hdc, TRANSPARENT );\r
1817     SetTextColor( hdc, chroma );\r
1818     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1819     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1820 \r
1821     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1822     /* Step 3: the area outside the piece is filled with white */\r
1823 //    FloodFill( hdc, 0, 0, chroma );\r
1824     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1825     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1826     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1827     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1828     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1829     /* \r
1830         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1831         but if the start point is not inside the piece we're lost!\r
1832         There should be a better way to do this... if we could create a region or path\r
1833         from the fill operation we would be fine for example.\r
1834     */\r
1835 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1836     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1837 \r
1838     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1839         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1840         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1841 \r
1842         SelectObject( dc2, bm2 );\r
1843         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1844         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1848 \r
1849         DeleteDC( dc2 );\r
1850         DeleteObject( bm2 );\r
1851     }\r
1852 \r
1853     SetTextColor( hdc, 0 );\r
1854     /* \r
1855         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1856         draw the piece again in black for safety.\r
1857     */\r
1858     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1859 \r
1860     SelectObject( hdc, hbm_old );\r
1861 \r
1862     if( hPieceMask[index] != NULL ) {\r
1863         DeleteObject( hPieceMask[index] );\r
1864     }\r
1865 \r
1866     hPieceMask[index] = hbm;\r
1867 \r
1868     /* Face */\r
1869     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1870 \r
1871     SelectObject( hdc, hbm );\r
1872 \r
1873     {\r
1874         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1875         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1876         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1877 \r
1878         SelectObject( dc1, hPieceMask[index] );\r
1879         SelectObject( dc2, bm2 );\r
1880         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1881         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1882         \r
1883         /* \r
1884             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1885             the piece background and deletes (makes transparent) the rest.\r
1886             Thanks to that mask, we are free to paint the background with the greates\r
1887             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1888             We use this, to make gradients and give the pieces a "roundish" look.\r
1889         */\r
1890         SetPieceBackground( hdc, backColor, 2 );\r
1891         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1892 \r
1893         DeleteDC( dc2 );\r
1894         DeleteDC( dc1 );\r
1895         DeleteObject( bm2 );\r
1896     }\r
1897 \r
1898     SetTextColor( hdc, foreColor );\r
1899     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1900 \r
1901     SelectObject( hdc, hbm_old );\r
1902 \r
1903     if( hPieceFace[index] != NULL ) {\r
1904         DeleteObject( hPieceFace[index] );\r
1905     }\r
1906 \r
1907     hPieceFace[index] = hbm;\r
1908 }\r
1909 \r
1910 static int TranslatePieceToFontPiece( int piece )\r
1911 {\r
1912     switch( piece ) {\r
1913     case BlackPawn:\r
1914         return PM_BP;\r
1915     case BlackKnight:\r
1916         return PM_BN;\r
1917     case BlackBishop:\r
1918         return PM_BB;\r
1919     case BlackRook:\r
1920         return PM_BR;\r
1921     case BlackQueen:\r
1922         return PM_BQ;\r
1923     case BlackKing:\r
1924         return PM_BK;\r
1925     case WhitePawn:\r
1926         return PM_WP;\r
1927     case WhiteKnight:\r
1928         return PM_WN;\r
1929     case WhiteBishop:\r
1930         return PM_WB;\r
1931     case WhiteRook:\r
1932         return PM_WR;\r
1933     case WhiteQueen:\r
1934         return PM_WQ;\r
1935     case WhiteKing:\r
1936         return PM_WK;\r
1937 \r
1938     case BlackAngel:\r
1939         return PM_BA;\r
1940     case BlackMarshall:\r
1941         return PM_BC;\r
1942     case BlackFerz:\r
1943         return PM_BF;\r
1944     case BlackNightrider:\r
1945         return PM_BH;\r
1946     case BlackAlfil:\r
1947         return PM_BE;\r
1948     case BlackWazir:\r
1949         return PM_BW;\r
1950     case BlackUnicorn:\r
1951         return PM_BU;\r
1952     case BlackCannon:\r
1953         return PM_BO;\r
1954     case BlackGrasshopper:\r
1955         return PM_BG;\r
1956     case BlackMan:\r
1957         return PM_BM;\r
1958     case BlackSilver:\r
1959         return PM_BSG;\r
1960     case BlackLance:\r
1961         return PM_BL;\r
1962     case BlackFalcon:\r
1963         return PM_BV;\r
1964     case BlackCobra:\r
1965         return PM_BS;\r
1966     case BlackCardinal:\r
1967         return PM_BAB;\r
1968     case BlackDragon:\r
1969         return PM_BD;\r
1970 \r
1971     case WhiteAngel:\r
1972         return PM_WA;\r
1973     case WhiteMarshall:\r
1974         return PM_WC;\r
1975     case WhiteFerz:\r
1976         return PM_WF;\r
1977     case WhiteNightrider:\r
1978         return PM_WH;\r
1979     case WhiteAlfil:\r
1980         return PM_WE;\r
1981     case WhiteWazir:\r
1982         return PM_WW;\r
1983     case WhiteUnicorn:\r
1984         return PM_WU;\r
1985     case WhiteCannon:\r
1986         return PM_WO;\r
1987     case WhiteGrasshopper:\r
1988         return PM_WG;\r
1989     case WhiteMan:\r
1990         return PM_WM;\r
1991     case WhiteSilver:\r
1992         return PM_WSG;\r
1993     case WhiteLance:\r
1994         return PM_WL;\r
1995     case WhiteFalcon:\r
1996         return PM_WV;\r
1997     case WhiteCobra:\r
1998         return PM_WS;\r
1999     case WhiteCardinal:\r
2000         return PM_WAB;\r
2001     case WhiteDragon:\r
2002         return PM_WD;\r
2003     }\r
2004 \r
2005     return 0;\r
2006 }\r
2007 \r
2008 void CreatePiecesFromFont()\r
2009 {\r
2010     LOGFONT lf;\r
2011     HDC hdc_window = NULL;\r
2012     HDC hdc = NULL;\r
2013     HFONT hfont_old;\r
2014     int fontHeight;\r
2015     int i;\r
2016 \r
2017     if( fontBitmapSquareSize < 0 ) {\r
2018         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2019         return;\r
2020     }\r
2021 \r
2022     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2023             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2024         fontBitmapSquareSize = -1;\r
2025         return;\r
2026     }\r
2027 \r
2028     if( fontBitmapSquareSize != squareSize ) {\r
2029         hdc_window = GetDC( hwndMain );\r
2030         hdc = CreateCompatibleDC( hdc_window );\r
2031 \r
2032         if( hPieceFont != NULL ) {\r
2033             DeleteObject( hPieceFont );\r
2034         }\r
2035         else {\r
2036             for( i=0; i<=(int)BlackKing; i++ ) {\r
2037                 hPieceMask[i] = NULL;\r
2038                 hPieceFace[i] = NULL;\r
2039             }\r
2040         }\r
2041 \r
2042         fontHeight = 75;\r
2043 \r
2044         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2045             fontHeight = appData.fontPieceSize;\r
2046         }\r
2047 \r
2048         fontHeight = (fontHeight * squareSize) / 100;\r
2049 \r
2050         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2051         lf.lfWidth = 0;\r
2052         lf.lfEscapement = 0;\r
2053         lf.lfOrientation = 0;\r
2054         lf.lfWeight = FW_NORMAL;\r
2055         lf.lfItalic = 0;\r
2056         lf.lfUnderline = 0;\r
2057         lf.lfStrikeOut = 0;\r
2058         lf.lfCharSet = DEFAULT_CHARSET;\r
2059         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2060         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2061         lf.lfQuality = PROOF_QUALITY;\r
2062         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2063         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2064         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2065 \r
2066         hPieceFont = CreateFontIndirect( &lf );\r
2067 \r
2068         if( hPieceFont == NULL ) {\r
2069             fontBitmapSquareSize = -2;\r
2070         }\r
2071         else {\r
2072             /* Setup font-to-piece character table */\r
2073             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2074                 /* No (or wrong) global settings, try to detect the font */\r
2075                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2076                     /* Alpha */\r
2077                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2078                 }\r
2079                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2080                     /* DiagramTT* family */\r
2081                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2082                 }\r
2083                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2084                     /* Fairy symbols */\r
2085                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2086                 }\r
2087                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2088                     /* Good Companion (Some characters get warped as literal :-( */\r
2089                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2090                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2091                     SetCharTable(pieceToFontChar, s);\r
2092                 }\r
2093                 else {\r
2094                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2095                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2096                 }\r
2097             }\r
2098 \r
2099             /* Create bitmaps */\r
2100             hfont_old = SelectObject( hdc, hPieceFont );\r
2101             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2102                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2103                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2104 \r
2105             SelectObject( hdc, hfont_old );\r
2106 \r
2107             fontBitmapSquareSize = squareSize;\r
2108         }\r
2109     }\r
2110 \r
2111     if( hdc != NULL ) {\r
2112         DeleteDC( hdc );\r
2113     }\r
2114 \r
2115     if( hdc_window != NULL ) {\r
2116         ReleaseDC( hwndMain, hdc_window );\r
2117     }\r
2118 }\r
2119 \r
2120 HBITMAP\r
2121 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2122 {\r
2123   char name[128], buf[MSG_SIZ];\r
2124 \r
2125     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2126   if(appData.pieceDirectory[0]) {\r
2127     HBITMAP res;\r
2128     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2129     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2130     if(res) return res;\r
2131   }\r
2132   if (gameInfo.event &&\r
2133       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2134       strcmp(name, "k80s") == 0) {\r
2135     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2136   }\r
2137   return LoadBitmap(hinst, name);\r
2138 }\r
2139 \r
2140 \r
2141 /* Insert a color into the program's logical palette\r
2142    structure.  This code assumes the given color is\r
2143    the result of the RGB or PALETTERGB macro, and it\r
2144    knows how those macros work (which is documented).\r
2145 */\r
2146 VOID\r
2147 InsertInPalette(COLORREF color)\r
2148 {\r
2149   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2150 \r
2151   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2152     DisplayFatalError(_("Too many colors"), 0, 1);\r
2153     pLogPal->palNumEntries--;\r
2154     return;\r
2155   }\r
2156 \r
2157   pe->peFlags = (char) 0;\r
2158   pe->peRed = (char) (0xFF & color);\r
2159   pe->peGreen = (char) (0xFF & (color >> 8));\r
2160   pe->peBlue = (char) (0xFF & (color >> 16));\r
2161   return;\r
2162 }\r
2163 \r
2164 \r
2165 VOID\r
2166 InitDrawingColors()\r
2167 {\r
2168   if (pLogPal == NULL) {\r
2169     /* Allocate enough memory for a logical palette with\r
2170      * PALETTESIZE entries and set the size and version fields\r
2171      * of the logical palette structure.\r
2172      */\r
2173     pLogPal = (NPLOGPALETTE)\r
2174       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2175                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2176     pLogPal->palVersion    = 0x300;\r
2177   }\r
2178   pLogPal->palNumEntries = 0;\r
2179 \r
2180   InsertInPalette(lightSquareColor);\r
2181   InsertInPalette(darkSquareColor);\r
2182   InsertInPalette(whitePieceColor);\r
2183   InsertInPalette(blackPieceColor);\r
2184   InsertInPalette(highlightSquareColor);\r
2185   InsertInPalette(premoveHighlightColor);\r
2186 \r
2187   /*  create a logical color palette according the information\r
2188    *  in the LOGPALETTE structure.\r
2189    */\r
2190   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2191 \r
2192   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2193   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2194   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2195   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2196   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2197   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2198   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2199   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2200   /* [AS] Force rendering of the font-based pieces */\r
2201   if( fontBitmapSquareSize > 0 ) {\r
2202     fontBitmapSquareSize = 0;\r
2203   }\r
2204 }\r
2205 \r
2206 \r
2207 int\r
2208 BoardWidth(int boardSize, int n)\r
2209 { /* [HGM] argument n added to allow different width and height */\r
2210   int lineGap = sizeInfo[boardSize].lineGap;\r
2211 \r
2212   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2213       lineGap = appData.overrideLineGap;\r
2214   }\r
2215 \r
2216   return (n + 1) * lineGap +\r
2217           n * sizeInfo[boardSize].squareSize;\r
2218 }\r
2219 \r
2220 /* Respond to board resize by dragging edge */\r
2221 VOID\r
2222 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2223 {\r
2224   BoardSize newSize = NUM_SIZES - 1;\r
2225   static int recurse = 0;\r
2226   if (IsIconic(hwndMain)) return;\r
2227   if (recurse > 0) return;\r
2228   recurse++;\r
2229   while (newSize > 0) {\r
2230         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2231         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2232            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2233     newSize--;\r
2234   } \r
2235   boardSize = newSize;\r
2236   InitDrawingSizes(boardSize, flags);\r
2237   recurse--;\r
2238 }\r
2239 \r
2240 \r
2241 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2242 \r
2243 VOID\r
2244 InitDrawingSizes(BoardSize boardSize, int flags)\r
2245 {\r
2246   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2247   ChessSquare piece;\r
2248   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2249   HDC hdc;\r
2250   SIZE clockSize, messageSize;\r
2251   HFONT oldFont;\r
2252   char buf[MSG_SIZ];\r
2253   char *str;\r
2254   HMENU hmenu = GetMenu(hwndMain);\r
2255   RECT crect, wrect, oldRect;\r
2256   int offby;\r
2257   LOGBRUSH logbrush;\r
2258   VariantClass v = gameInfo.variant;\r
2259 \r
2260   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2261   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2262 \r
2263   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2264   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2265   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2266   oldBoardSize = boardSize;\r
2267 \r
2268   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2269   { // correct board size to one where built-in pieces exist\r
2270     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2271        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2272       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2273       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2274       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2275       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2276       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2277                                    boardSize = SizeMiddling;\r
2278     }\r
2279   }\r
2280   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2281 \r
2282   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2283   oldRect.top = wpMain.y;\r
2284   oldRect.right = wpMain.x + wpMain.width;\r
2285   oldRect.bottom = wpMain.y + wpMain.height;\r
2286 \r
2287   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2288   smallLayout = sizeInfo[boardSize].smallLayout;\r
2289   squareSize = sizeInfo[boardSize].squareSize;\r
2290   lineGap = sizeInfo[boardSize].lineGap;\r
2291   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2292   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2293 \r
2294   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2295       lineGap = appData.overrideLineGap;\r
2296   }\r
2297 \r
2298   if (tinyLayout != oldTinyLayout) {\r
2299     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2300     if (tinyLayout) {\r
2301       style &= ~WS_SYSMENU;\r
2302       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2303                  "&Minimize\tCtrl+F4");\r
2304     } else {\r
2305       style |= WS_SYSMENU;\r
2306       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2307     }\r
2308     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2309 \r
2310     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2311       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2312         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2313     }\r
2314     DrawMenuBar(hwndMain);\r
2315   }\r
2316 \r
2317   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2318   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2319 \r
2320   /* Get text area sizes */\r
2321   hdc = GetDC(hwndMain);\r
2322   if (appData.clockMode) {\r
2323     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2324   } else {\r
2325     snprintf(buf, MSG_SIZ, _("White"));\r
2326   }\r
2327   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2328   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2329   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2330   str = _("We only care about the height here");\r
2331   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2332   SelectObject(hdc, oldFont);\r
2333   ReleaseDC(hwndMain, hdc);\r
2334 \r
2335   /* Compute where everything goes */\r
2336   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2337         /* [HGM] logo: if either logo is on, reserve space for it */\r
2338         logoHeight =  2*clockSize.cy;\r
2339         leftLogoRect.left   = OUTER_MARGIN;\r
2340         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2341         leftLogoRect.top    = OUTER_MARGIN;\r
2342         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2343 \r
2344         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2345         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2346         rightLogoRect.top    = OUTER_MARGIN;\r
2347         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2348 \r
2349 \r
2350     whiteRect.left = leftLogoRect.right;\r
2351     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2352     whiteRect.top = OUTER_MARGIN;\r
2353     whiteRect.bottom = whiteRect.top + logoHeight;\r
2354 \r
2355     blackRect.right = rightLogoRect.left;\r
2356     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2357     blackRect.top = whiteRect.top;\r
2358     blackRect.bottom = whiteRect.bottom;\r
2359   } else {\r
2360     whiteRect.left = OUTER_MARGIN;\r
2361     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2362     whiteRect.top = OUTER_MARGIN;\r
2363     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2364 \r
2365     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2366     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2367     blackRect.top = whiteRect.top;\r
2368     blackRect.bottom = whiteRect.bottom;\r
2369 \r
2370     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2371   }\r
2372 \r
2373   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2374   if (appData.showButtonBar) {\r
2375     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2376       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2377   } else {\r
2378     messageRect.right = OUTER_MARGIN + boardWidth;\r
2379   }\r
2380   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2381   messageRect.bottom = messageRect.top + messageSize.cy;\r
2382 \r
2383   boardRect.left = OUTER_MARGIN;\r
2384   boardRect.right = boardRect.left + boardWidth;\r
2385   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2386   boardRect.bottom = boardRect.top + boardHeight;\r
2387 \r
2388   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2389   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2390   oldTinyLayout = tinyLayout;\r
2391   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2392   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2393     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2394   winW *= 1 + twoBoards;\r
2395   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2396   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2397   wpMain.height = winH; //       without disturbing window attachments\r
2398   GetWindowRect(hwndMain, &wrect);\r
2399   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2400                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2401 \r
2402   // [HGM] placement: let attached windows follow size change.\r
2403   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2404   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2408 \r
2409   /* compensate if menu bar wrapped */\r
2410   GetClientRect(hwndMain, &crect);\r
2411   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2412   wpMain.height += offby;\r
2413   switch (flags) {\r
2414   case WMSZ_TOPLEFT:\r
2415     SetWindowPos(hwndMain, NULL, \r
2416                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2417                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2418     break;\r
2419 \r
2420   case WMSZ_TOPRIGHT:\r
2421   case WMSZ_TOP:\r
2422     SetWindowPos(hwndMain, NULL, \r
2423                  wrect.left, wrect.bottom - wpMain.height, \r
2424                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2425     break;\r
2426 \r
2427   case WMSZ_BOTTOMLEFT:\r
2428   case WMSZ_LEFT:\r
2429     SetWindowPos(hwndMain, NULL, \r
2430                  wrect.right - wpMain.width, wrect.top, \r
2431                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2432     break;\r
2433 \r
2434   case WMSZ_BOTTOMRIGHT:\r
2435   case WMSZ_BOTTOM:\r
2436   case WMSZ_RIGHT:\r
2437   default:\r
2438     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2439                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2440     break;\r
2441   }\r
2442 \r
2443   hwndPause = NULL;\r
2444   for (i = 0; i < N_BUTTONS; i++) {\r
2445     if (buttonDesc[i].hwnd != NULL) {\r
2446       DestroyWindow(buttonDesc[i].hwnd);\r
2447       buttonDesc[i].hwnd = NULL;\r
2448     }\r
2449     if (appData.showButtonBar) {\r
2450       buttonDesc[i].hwnd =\r
2451         CreateWindow("BUTTON", buttonDesc[i].label,\r
2452                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2453                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2454                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2455                      (HMENU) buttonDesc[i].id,\r
2456                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2457       if (tinyLayout) {\r
2458         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2459                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2460                     MAKELPARAM(FALSE, 0));\r
2461       }\r
2462       if (buttonDesc[i].id == IDM_Pause)\r
2463         hwndPause = buttonDesc[i].hwnd;\r
2464       buttonDesc[i].wndproc = (WNDPROC)\r
2465         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2466     }\r
2467   }\r
2468   if (gridPen != NULL) DeleteObject(gridPen);\r
2469   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2470   if (premovePen != NULL) DeleteObject(premovePen);\r
2471   if (lineGap != 0) {\r
2472     logbrush.lbStyle = BS_SOLID;\r
2473     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2474     gridPen =\r
2475       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2476                    lineGap, &logbrush, 0, NULL);\r
2477     logbrush.lbColor = highlightSquareColor;\r
2478     highlightPen =\r
2479       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2480                    lineGap, &logbrush, 0, NULL);\r
2481 \r
2482     logbrush.lbColor = premoveHighlightColor; \r
2483     premovePen =\r
2484       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2485                    lineGap, &logbrush, 0, NULL);\r
2486 \r
2487     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2488     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2489       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2490       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2491         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2492       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2493         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2494       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2495     }\r
2496     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2497       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2498       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2499         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2500         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2501       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2502         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2503       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2504     }\r
2505   }\r
2506 \r
2507   /* [HGM] Licensing requirement */\r
2508 #ifdef GOTHIC\r
2509   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2510 #endif\r
2511 #ifdef FALCON\r
2512   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2513 #endif\r
2514   GothicPopUp( "", VariantNormal);\r
2515 \r
2516 \r
2517 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2518 \r
2519   /* Load piece bitmaps for this board size */\r
2520   for (i=0; i<=2; i++) {\r
2521     for (piece = WhitePawn;\r
2522          (int) piece < (int) BlackPawn;\r
2523          piece = (ChessSquare) ((int) piece + 1)) {\r
2524       if (pieceBitmap[i][piece] != NULL)\r
2525         DeleteObject(pieceBitmap[i][piece]);\r
2526     }\r
2527   }\r
2528 \r
2529   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2530   // Orthodox Chess pieces\r
2531   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2532   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2533   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2534   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2535   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2536   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2537   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2538   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2539   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2540   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2541   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2542   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2543   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2544   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2545   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2546   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2547     // in Shogi, Hijack the unused Queen for Lance\r
2548     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2549     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2550     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2551   } else {\r
2552     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2553     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2554     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2555   }\r
2556 \r
2557   if(squareSize <= 72 && squareSize >= 33) { \r
2558     /* A & C are available in most sizes now */\r
2559     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2560       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2561       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2562       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2563       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2564       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2565       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2566       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2567       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2568       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2569       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2570       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2571       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2572     } else { // Smirf-like\r
2573       if(gameInfo.variant == VariantSChess) {\r
2574         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2575         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2576         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2577       } else {\r
2578         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2579         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2580         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2581       }\r
2582     }\r
2583     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2584       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2585       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2586       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2587     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2588       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2589       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2590       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2591     } else { // WinBoard standard\r
2592       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2593       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2594       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2595     }\r
2596   }\r
2597 \r
2598 \r
2599   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2600     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2601     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2602     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2603     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2604     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2605     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2606     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2607     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2608     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2609     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2610     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2611     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2612     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2613     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2614     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2615     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2616     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2617     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2618     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2619     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2620     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2621     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2622     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2623     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2624     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2625     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2626     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2627     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2628     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2629     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2630 \r
2631     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2632       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2633       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2634       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2635       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2636       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2637       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2638       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2639       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2640       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2641       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2642       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2643       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2644     } else {\r
2645       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2646       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2647       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2648       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2649       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2650       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2651       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2652       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2653       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2654       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2655       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2656       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2657     }\r
2658 \r
2659   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2660     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2661     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2662     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2663     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2664     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2665     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2666     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2667     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2668     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2669     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2670     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2671     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2672     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2673     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2674   }\r
2675 \r
2676 \r
2677   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2678   /* special Shogi support in this size */\r
2679   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2680       for (piece = WhitePawn;\r
2681            (int) piece < (int) BlackPawn;\r
2682            piece = (ChessSquare) ((int) piece + 1)) {\r
2683         if (pieceBitmap[i][piece] != NULL)\r
2684           DeleteObject(pieceBitmap[i][piece]);\r
2685       }\r
2686     }\r
2687   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2688   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2689   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2690   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2691   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2692   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2693   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2694   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2695   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2696   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2697   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2698   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2699   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2700   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2701   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2702   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2703   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2704   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2705   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2706   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2707   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2708   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2709   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2710   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2711   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2712   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2713   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2714   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2715   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2716   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2717   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2718   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2719   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2720   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2721   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2722   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2723   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2724   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2725   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2726   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2727   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2728   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2729   minorSize = 0;\r
2730   }\r
2731 }\r
2732 \r
2733 HBITMAP\r
2734 PieceBitmap(ChessSquare p, int kind)\r
2735 {\r
2736   if ((int) p >= (int) BlackPawn)\r
2737     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2738 \r
2739   return pieceBitmap[kind][(int) p];\r
2740 }\r
2741 \r
2742 /***************************************************************/\r
2743 \r
2744 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2745 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2746 /*\r
2747 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2748 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2749 */\r
2750 \r
2751 VOID\r
2752 SquareToPos(int row, int column, int * x, int * y)\r
2753 {\r
2754   if (flipView) {\r
2755     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2756     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2757   } else {\r
2758     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2759     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2760   }\r
2761 }\r
2762 \r
2763 VOID\r
2764 DrawCoordsOnDC(HDC hdc)\r
2765 {\r
2766   static char files[] = "0123456789012345678901221098765432109876543210";\r
2767   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2768   char str[2] = { NULLCHAR, NULLCHAR };\r
2769   int oldMode, oldAlign, x, y, start, i;\r
2770   HFONT oldFont;\r
2771   HBRUSH oldBrush;\r
2772 \r
2773   if (!appData.showCoords)\r
2774     return;\r
2775 \r
2776   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2777 \r
2778   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2779   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2780   oldAlign = GetTextAlign(hdc);\r
2781   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2782 \r
2783   y = boardRect.top + lineGap;\r
2784   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2785 \r
2786   if(border) {\r
2787     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2788     x += border - lineGap - 4; y += squareSize - 6;\r
2789   } else\r
2790   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2791   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2792     str[0] = files[start + i];\r
2793     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2794     y += squareSize + lineGap;\r
2795   }\r
2796 \r
2797   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2798 \r
2799   if(border) {\r
2800     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2801     x += -border + 4; y += border - squareSize + 6;\r
2802   } else\r
2803   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2804   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2805     str[0] = ranks[start + i];\r
2806     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2807     x += squareSize + lineGap;\r
2808   }    \r
2809 \r
2810   SelectObject(hdc, oldBrush);\r
2811   SetBkMode(hdc, oldMode);\r
2812   SetTextAlign(hdc, oldAlign);\r
2813   SelectObject(hdc, oldFont);\r
2814 }\r
2815 \r
2816 VOID\r
2817 DrawGridOnDC(HDC hdc)\r
2818 {\r
2819   HPEN oldPen;\r
2820  \r
2821   if (lineGap != 0) {\r
2822     oldPen = SelectObject(hdc, gridPen);\r
2823     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2824     SelectObject(hdc, oldPen);\r
2825   }\r
2826 }\r
2827 \r
2828 #define HIGHLIGHT_PEN 0\r
2829 #define PREMOVE_PEN   1\r
2830 \r
2831 VOID\r
2832 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2833 {\r
2834   int x1, y1;\r
2835   HPEN oldPen, hPen;\r
2836   if (lineGap == 0) return;\r
2837   if (flipView) {\r
2838     x1 = boardRect.left +\r
2839       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2840     y1 = boardRect.top +\r
2841       lineGap/2 + y * (squareSize + lineGap) + border;\r
2842   } else {\r
2843     x1 = boardRect.left +\r
2844       lineGap/2 + x * (squareSize + lineGap) + border;\r
2845     y1 = boardRect.top +\r
2846       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2847   }\r
2848   hPen = pen ? premovePen : highlightPen;\r
2849   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2850   MoveToEx(hdc, x1, y1, NULL);\r
2851   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2852   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2853   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2854   LineTo(hdc, x1, y1);\r
2855   SelectObject(hdc, oldPen);\r
2856 }\r
2857 \r
2858 VOID\r
2859 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2860 {\r
2861   int i;\r
2862   for (i=0; i<2; i++) {\r
2863     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2864       DrawHighlightOnDC(hdc, TRUE,\r
2865                         h->sq[i].x, h->sq[i].y,\r
2866                         pen);\r
2867   }\r
2868 }\r
2869 \r
2870 /* Note: sqcolor is used only in monoMode */\r
2871 /* Note that this code is largely duplicated in woptions.c,\r
2872    function DrawSampleSquare, so that needs to be updated too */\r
2873 VOID\r
2874 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2875 {\r
2876   HBITMAP oldBitmap;\r
2877   HBRUSH oldBrush;\r
2878   int tmpSize;\r
2879 \r
2880   if (appData.blindfold) return;\r
2881 \r
2882   /* [AS] Use font-based pieces if needed */\r
2883   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2884     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2885     CreatePiecesFromFont();\r
2886 \r
2887     if( fontBitmapSquareSize == squareSize ) {\r
2888         int index = TranslatePieceToFontPiece(piece);\r
2889 \r
2890         SelectObject( tmphdc, hPieceMask[ index ] );\r
2891 \r
2892       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2893         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2894       else\r
2895         BitBlt( hdc,\r
2896             x, y,\r
2897             squareSize, squareSize,\r
2898             tmphdc,\r
2899             0, 0,\r
2900             SRCAND );\r
2901 \r
2902         SelectObject( tmphdc, hPieceFace[ index ] );\r
2903 \r
2904       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2905         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2906       else\r
2907         BitBlt( hdc,\r
2908             x, y,\r
2909             squareSize, squareSize,\r
2910             tmphdc,\r
2911             0, 0,\r
2912             SRCPAINT );\r
2913 \r
2914         return;\r
2915     }\r
2916   }\r
2917 \r
2918   if (appData.monoMode) {\r
2919     SelectObject(tmphdc, PieceBitmap(piece, \r
2920       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2921     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2922            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2923   } else {\r
2924     HBRUSH xBrush = whitePieceBrush;\r
2925     tmpSize = squareSize;\r
2926     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2927     if(minorSize &&\r
2928         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2929          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2930       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2931       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2932       x += (squareSize - minorSize)>>1;\r
2933       y += squareSize - minorSize - 2;\r
2934       tmpSize = minorSize;\r
2935     }\r
2936     if (color || appData.allWhite ) {\r
2937       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2938       if( color )\r
2939               oldBrush = SelectObject(hdc, xBrush);\r
2940       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2941       if(appData.upsideDown && color==flipView)\r
2942         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2943       else\r
2944         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2945       /* Use black for outline of white pieces */\r
2946       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2947       if(appData.upsideDown && color==flipView)\r
2948         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2949       else\r
2950         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2951     } else if(appData.pieceDirectory[0]) {\r
2952       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2953       oldBrush = SelectObject(hdc, xBrush);\r
2954       if(appData.upsideDown && color==flipView)\r
2955         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2956       else\r
2957         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2958       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2959       if(appData.upsideDown && color==flipView)\r
2960         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2961       else\r
2962         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2963     } else {\r
2964       /* Use square color for details of black pieces */\r
2965       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2966       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2967       if(appData.upsideDown && !flipView)\r
2968         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2969       else\r
2970         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2971     }\r
2972     SelectObject(hdc, oldBrush);\r
2973     SelectObject(tmphdc, oldBitmap);\r
2974   }\r
2975 }\r
2976 \r
2977 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2978 int GetBackTextureMode( int algo )\r
2979 {\r
2980     int result = BACK_TEXTURE_MODE_DISABLED;\r
2981 \r
2982     switch( algo ) \r
2983     {\r
2984         case BACK_TEXTURE_MODE_PLAIN:\r
2985             result = 1; /* Always use identity map */\r
2986             break;\r
2987         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2988             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2989             break;\r
2990     }\r
2991 \r
2992     return result;\r
2993 }\r
2994 \r
2995 /* \r
2996     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2997     to handle redraws cleanly (as random numbers would always be different).\r
2998 */\r
2999 VOID RebuildTextureSquareInfo()\r
3000 {\r
3001     BITMAP bi;\r
3002     int lite_w = 0;\r
3003     int lite_h = 0;\r
3004     int dark_w = 0;\r
3005     int dark_h = 0;\r
3006     int row;\r
3007     int col;\r
3008 \r
3009     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3010 \r
3011     if( liteBackTexture != NULL ) {\r
3012         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3013             lite_w = bi.bmWidth;\r
3014             lite_h = bi.bmHeight;\r
3015         }\r
3016     }\r
3017 \r
3018     if( darkBackTexture != NULL ) {\r
3019         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3020             dark_w = bi.bmWidth;\r
3021             dark_h = bi.bmHeight;\r
3022         }\r
3023     }\r
3024 \r
3025     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3026         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3027             if( (col + row) & 1 ) {\r
3028                 /* Lite square */\r
3029                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3030                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3031                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3032                   else\r
3033                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3034                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3035                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3036                   else\r
3037                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3038                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3039                 }\r
3040             }\r
3041             else {\r
3042                 /* Dark square */\r
3043                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3044                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3045                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3046                   else\r
3047                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3048                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3049                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3050                   else\r
3051                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3052                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3053                 }\r
3054             }\r
3055         }\r
3056     }\r
3057 }\r
3058 \r
3059 /* [AS] Arrow highlighting support */\r
3060 \r
3061 static double A_WIDTH = 5; /* Width of arrow body */\r
3062 \r
3063 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3064 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3065 \r
3066 static double Sqr( double x )\r
3067 {\r
3068     return x*x;\r
3069 }\r
3070 \r
3071 static int Round( double x )\r
3072 {\r
3073     return (int) (x + 0.5);\r
3074 }\r
3075 \r
3076 /* Draw an arrow between two points using current settings */\r
3077 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3078 {\r
3079     POINT arrow[7];\r
3080     double dx, dy, j, k, x, y;\r
3081 \r
3082     if( d_x == s_x ) {\r
3083         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3084 \r
3085         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3086         arrow[0].y = s_y;\r
3087 \r
3088         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3089         arrow[1].y = d_y - h;\r
3090 \r
3091         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3092         arrow[2].y = d_y - h;\r
3093 \r
3094         arrow[3].x = d_x;\r
3095         arrow[3].y = d_y;\r
3096 \r
3097         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3098         arrow[5].y = d_y - h;\r
3099 \r
3100         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3101         arrow[4].y = d_y - h;\r
3102 \r
3103         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3104         arrow[6].y = s_y;\r
3105     }\r
3106     else if( d_y == s_y ) {\r
3107         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3108 \r
3109         arrow[0].x = s_x;\r
3110         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3111 \r
3112         arrow[1].x = d_x - w;\r
3113         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3114 \r
3115         arrow[2].x = d_x - w;\r
3116         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3117 \r
3118         arrow[3].x = d_x;\r
3119         arrow[3].y = d_y;\r
3120 \r
3121         arrow[5].x = d_x - w;\r
3122         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3123 \r
3124         arrow[4].x = d_x - w;\r
3125         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3126 \r
3127         arrow[6].x = s_x;\r
3128         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3129     }\r
3130     else {\r
3131         /* [AS] Needed a lot of paper for this! :-) */\r
3132         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3133         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3134   \r
3135         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3136 \r
3137         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3138 \r
3139         x = s_x;\r
3140         y = s_y;\r
3141 \r
3142         arrow[0].x = Round(x - j);\r
3143         arrow[0].y = Round(y + j*dx);\r
3144 \r
3145         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3146         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3147 \r
3148         if( d_x > s_x ) {\r
3149             x = (double) d_x - k;\r
3150             y = (double) d_y - k*dy;\r
3151         }\r
3152         else {\r
3153             x = (double) d_x + k;\r
3154             y = (double) d_y + k*dy;\r
3155         }\r
3156 \r
3157         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3158 \r
3159         arrow[6].x = Round(x - j);\r
3160         arrow[6].y = Round(y + j*dx);\r
3161 \r
3162         arrow[2].x = Round(arrow[6].x + 2*j);\r
3163         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3164 \r
3165         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3166         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3167 \r
3168         arrow[4].x = d_x;\r
3169         arrow[4].y = d_y;\r
3170 \r
3171         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3172         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3173     }\r
3174 \r
3175     Polygon( hdc, arrow, 7 );\r
3176 }\r
3177 \r
3178 /* [AS] Draw an arrow between two squares */\r
3179 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3180 {\r
3181     int s_x, s_y, d_x, d_y;\r
3182     HPEN hpen;\r
3183     HPEN holdpen;\r
3184     HBRUSH hbrush;\r
3185     HBRUSH holdbrush;\r
3186     LOGBRUSH stLB;\r
3187 \r
3188     if( s_col == d_col && s_row == d_row ) {\r
3189         return;\r
3190     }\r
3191 \r
3192     /* Get source and destination points */\r
3193     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3194     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3195 \r
3196     if( d_y > s_y ) {\r
3197         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3198     }\r
3199     else if( d_y < s_y ) {\r
3200         d_y += squareSize / 2 + squareSize / 4;\r
3201     }\r
3202     else {\r
3203         d_y += squareSize / 2;\r
3204     }\r
3205 \r
3206     if( d_x > s_x ) {\r
3207         d_x += squareSize / 2 - squareSize / 4;\r
3208     }\r
3209     else if( d_x < s_x ) {\r
3210         d_x += squareSize / 2 + squareSize / 4;\r
3211     }\r
3212     else {\r
3213         d_x += squareSize / 2;\r
3214     }\r
3215 \r
3216     s_x += squareSize / 2;\r
3217     s_y += squareSize / 2;\r
3218 \r
3219     /* Adjust width */\r
3220     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3221 \r
3222     /* Draw */\r
3223     stLB.lbStyle = BS_SOLID;\r
3224     stLB.lbColor = appData.highlightArrowColor;\r
3225     stLB.lbHatch = 0;\r
3226 \r
3227     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3228     holdpen = SelectObject( hdc, hpen );\r
3229     hbrush = CreateBrushIndirect( &stLB );\r
3230     holdbrush = SelectObject( hdc, hbrush );\r
3231 \r
3232     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3233 \r
3234     SelectObject( hdc, holdpen );\r
3235     SelectObject( hdc, holdbrush );\r
3236     DeleteObject( hpen );\r
3237     DeleteObject( hbrush );\r
3238 }\r
3239 \r
3240 BOOL HasHighlightInfo()\r
3241 {\r
3242     BOOL result = FALSE;\r
3243 \r
3244     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3245         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3246     {\r
3247         result = TRUE;\r
3248     }\r
3249 \r
3250     return result;\r
3251 }\r
3252 \r
3253 BOOL IsDrawArrowEnabled()\r
3254 {\r
3255     BOOL result = FALSE;\r
3256 \r
3257     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3258         result = TRUE;\r
3259     }\r
3260 \r
3261     return result;\r
3262 }\r
3263 \r
3264 VOID DrawArrowHighlight( HDC hdc )\r
3265 {\r
3266     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3267         DrawArrowBetweenSquares( hdc,\r
3268             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3269             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3270     }\r
3271 }\r
3272 \r
3273 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3274 {\r
3275     HRGN result = NULL;\r
3276 \r
3277     if( HasHighlightInfo() ) {\r
3278         int x1, y1, x2, y2;\r
3279         int sx, sy, dx, dy;\r
3280 \r
3281         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3282         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3283 \r
3284         sx = MIN( x1, x2 );\r
3285         sy = MIN( y1, y2 );\r
3286         dx = MAX( x1, x2 ) + squareSize;\r
3287         dy = MAX( y1, y2 ) + squareSize;\r
3288 \r
3289         result = CreateRectRgn( sx, sy, dx, dy );\r
3290     }\r
3291 \r
3292     return result;\r
3293 }\r
3294 \r
3295 /*\r
3296     Warning: this function modifies the behavior of several other functions. \r
3297     \r
3298     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3299     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3300     repaint is scattered all over the place, which is not good for features such as\r
3301     "arrow highlighting" that require a full repaint of the board.\r
3302 \r
3303     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3304     user interaction, when speed is not so important) but especially to avoid errors\r
3305     in the displayed graphics.\r
3306 \r
3307     In such patched places, I always try refer to this function so there is a single\r
3308     place to maintain knowledge.\r
3309     \r
3310     To restore the original behavior, just return FALSE unconditionally.\r
3311 */\r
3312 BOOL IsFullRepaintPreferrable()\r
3313 {\r
3314     BOOL result = FALSE;\r
3315 \r
3316     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3317         /* Arrow may appear on the board */\r
3318         result = TRUE;\r
3319     }\r
3320 \r
3321     return result;\r
3322 }\r
3323 \r
3324 /* \r
3325     This function is called by DrawPosition to know whether a full repaint must\r
3326     be forced or not.\r
3327 \r
3328     Only DrawPosition may directly call this function, which makes use of \r
3329     some state information. Other function should call DrawPosition specifying \r
3330     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3331 */\r
3332 BOOL DrawPositionNeedsFullRepaint()\r
3333 {\r
3334     BOOL result = FALSE;\r
3335 \r
3336     /* \r
3337         Probably a slightly better policy would be to trigger a full repaint\r
3338         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3339         but animation is fast enough that it's difficult to notice.\r
3340     */\r
3341     if( animInfo.piece == EmptySquare ) {\r
3342         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3343             result = TRUE;\r
3344         }\r
3345     }\r
3346 \r
3347     return result;\r
3348 }\r
3349 \r
3350 static HBITMAP borderBitmap;\r
3351 \r
3352 VOID\r
3353 DrawBackgroundOnDC(HDC hdc)\r
3354 {\r
3355   \r
3356   BITMAP bi;\r
3357   HDC tmphdc;\r
3358   HBITMAP hbm;\r
3359   static char oldBorder[MSG_SIZ];\r
3360   int w = 600, h = 600, mode;\r
3361 \r
3362   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3363     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3364     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3365   }\r
3366   if(borderBitmap == NULL) { // loading failed, use white\r
3367     FillRect( hdc, &boardRect, whitePieceBrush );\r
3368     return;\r
3369   }\r
3370   tmphdc = CreateCompatibleDC(hdc);\r
3371   hbm = SelectObject(tmphdc, borderBitmap);\r
3372   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3373             w = bi.bmWidth;\r
3374             h = bi.bmHeight;\r
3375   }\r
3376   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3377   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3378                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3379   SetStretchBltMode(hdc, mode);\r
3380   SelectObject(tmphdc, hbm);\r
3381   DeleteDC(tmphdc);\r
3382 }\r
3383 \r
3384 VOID\r
3385 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3386 {\r
3387   int row, column, x, y, square_color, piece_color;\r
3388   ChessSquare piece;\r
3389   HBRUSH oldBrush;\r
3390   HDC texture_hdc = NULL;\r
3391 \r
3392   /* [AS] Initialize background textures if needed */\r
3393   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3394       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3395       if( backTextureSquareSize != squareSize \r
3396        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3397           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3398           backTextureSquareSize = squareSize;\r
3399           RebuildTextureSquareInfo();\r
3400       }\r
3401 \r
3402       texture_hdc = CreateCompatibleDC( hdc );\r
3403   }\r
3404 \r
3405   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3406     for (column = 0; column < BOARD_WIDTH; column++) {\r
3407   \r
3408       SquareToPos(row, column, &x, &y);\r
3409 \r
3410       piece = board[row][column];\r
3411 \r
3412       square_color = ((column + row) % 2) == 1;\r
3413       if( gameInfo.variant == VariantXiangqi ) {\r
3414           square_color = !InPalace(row, column);\r
3415           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3416           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3417       }\r
3418       piece_color = (int) piece < (int) BlackPawn;\r
3419 \r
3420 \r
3421       /* [HGM] holdings file: light square or black */\r
3422       if(column == BOARD_LEFT-2) {\r
3423             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3424                 square_color = 1;\r
3425             else {\r
3426                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3427                 continue;\r
3428             }\r
3429       } else\r
3430       if(column == BOARD_RGHT + 1 ) {\r
3431             if( row < gameInfo.holdingsSize )\r
3432                 square_color = 1;\r
3433             else {\r
3434                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3435                 continue;\r
3436             }\r
3437       }\r
3438       if(column == BOARD_LEFT-1 ) /* left align */\r
3439             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3440       else if( column == BOARD_RGHT) /* right align */\r
3441             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3442       else\r
3443       if (appData.monoMode) {\r
3444         if (piece == EmptySquare) {\r
3445           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3446                  square_color ? WHITENESS : BLACKNESS);\r
3447         } else {\r
3448           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3449         }\r
3450       } \r
3451       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3452           /* [AS] Draw the square using a texture bitmap */\r
3453           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3454           int r = row, c = column; // [HGM] do not flip board in flipView\r
3455           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3456 \r
3457           DrawTile( x, y, \r
3458               squareSize, squareSize, \r
3459               hdc, \r
3460               texture_hdc,\r
3461               backTextureSquareInfo[r][c].mode,\r
3462               backTextureSquareInfo[r][c].x,\r
3463               backTextureSquareInfo[r][c].y );\r
3464 \r
3465           SelectObject( texture_hdc, hbm );\r
3466 \r
3467           if (piece != EmptySquare) {\r
3468               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3469           }\r
3470       }\r
3471       else {\r
3472         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3473 \r
3474         oldBrush = SelectObject(hdc, brush );\r
3475         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3476         SelectObject(hdc, oldBrush);\r
3477         if (piece != EmptySquare)\r
3478           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3479       }\r
3480     }\r
3481   }\r
3482 \r
3483   if( texture_hdc != NULL ) {\r
3484     DeleteDC( texture_hdc );\r
3485   }\r
3486 }\r
3487 \r
3488 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3489 void fputDW(FILE *f, int x)\r
3490 {\r
3491         fputc(x     & 255, f);\r
3492         fputc(x>>8  & 255, f);\r
3493         fputc(x>>16 & 255, f);\r
3494         fputc(x>>24 & 255, f);\r
3495 }\r
3496 \r
3497 #define MAX_CLIPS 200   /* more than enough */\r
3498 \r
3499 VOID\r
3500 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3501 {\r
3502 //  HBITMAP bufferBitmap;\r
3503   BITMAP bi;\r
3504 //  RECT Rect;\r
3505   HDC tmphdc;\r
3506   HBITMAP hbm;\r
3507   int w = 100, h = 50;\r
3508 \r
3509   if(logo == NULL) {\r
3510     if(!logoHeight) return;\r
3511     FillRect( hdc, &logoRect, whitePieceBrush );\r
3512   }\r
3513 //  GetClientRect(hwndMain, &Rect);\r
3514 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3515 //                                      Rect.bottom-Rect.top+1);\r
3516   tmphdc = CreateCompatibleDC(hdc);\r
3517   hbm = SelectObject(tmphdc, logo);\r
3518   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3519             w = bi.bmWidth;\r
3520             h = bi.bmHeight;\r
3521   }\r
3522   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3523                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3524   SelectObject(tmphdc, hbm);\r
3525   DeleteDC(tmphdc);\r
3526 }\r
3527 \r
3528 VOID\r
3529 DisplayLogos()\r
3530 {\r
3531   if(logoHeight) {\r
3532         HDC hdc = GetDC(hwndMain);\r
3533         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3534         if(appData.autoLogo) {\r
3535           \r
3536           switch(gameMode) { // pick logos based on game mode\r
3537             case IcsObserving:\r
3538                 whiteLogo = second.programLogo; // ICS logo\r
3539                 blackLogo = second.programLogo;\r
3540             default:\r
3541                 break;\r
3542             case IcsPlayingWhite:\r
3543                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3544                 blackLogo = second.programLogo; // ICS logo\r
3545                 break;\r
3546             case IcsPlayingBlack:\r
3547                 whiteLogo = second.programLogo; // ICS logo\r
3548                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3549                 break;\r
3550             case TwoMachinesPlay:\r
3551                 if(first.twoMachinesColor[0] == 'b') {\r
3552                     whiteLogo = second.programLogo;\r
3553                     blackLogo = first.programLogo;\r
3554                 }\r
3555                 break;\r
3556             case MachinePlaysWhite:\r
3557                 blackLogo = userLogo;\r
3558                 break;\r
3559             case MachinePlaysBlack:\r
3560                 whiteLogo = userLogo;\r
3561                 blackLogo = first.programLogo;\r
3562           }\r
3563         }\r
3564         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3565         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3566         ReleaseDC(hwndMain, hdc);\r
3567   }\r
3568 }\r
3569 \r
3570 void\r
3571 UpdateLogos(int display)\r
3572 { // called after loading new engine(s), in tourney or from menu\r
3573   LoadLogo(&first, 0, FALSE);\r
3574   LoadLogo(&second, 1, appData.icsActive);\r
3575   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3576   if(display) DisplayLogos();\r
3577 }\r
3578 \r
3579 static HDC hdcSeek;\r
3580 \r
3581 // [HGM] seekgraph\r
3582 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3583 {\r
3584     POINT stPt;\r
3585     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3586     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3587     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3588     SelectObject( hdcSeek, hp );\r
3589 }\r
3590 \r
3591 // front-end wrapper for drawing functions to do rectangles\r
3592 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3593 {\r
3594     HPEN hp;\r
3595     RECT rc;\r
3596 \r
3597     if (hdcSeek == NULL) {\r
3598     hdcSeek = GetDC(hwndMain);\r
3599       if (!appData.monoMode) {\r
3600         SelectPalette(hdcSeek, hPal, FALSE);\r
3601         RealizePalette(hdcSeek);\r
3602       }\r
3603     }\r
3604     hp = SelectObject( hdcSeek, gridPen );\r
3605     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3606     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3607     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3608     SelectObject( hdcSeek, hp );\r
3609 }\r
3610 \r
3611 // front-end wrapper for putting text in graph\r
3612 void DrawSeekText(char *buf, int x, int y)\r
3613 {\r
3614         SIZE stSize;\r
3615         SetBkMode( hdcSeek, TRANSPARENT );\r
3616         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3617         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3618 }\r
3619 \r
3620 void DrawSeekDot(int x, int y, int color)\r
3621 {\r
3622         int square = color & 0x80;\r
3623         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3624                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3625         color &= 0x7F;\r
3626         if(square)\r
3627             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3628                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3629         else\r
3630             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3631                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3632             SelectObject(hdcSeek, oldBrush);\r
3633 }\r
3634 \r
3635 void DrawSeekOpen()\r
3636 {\r
3637 }\r
3638 \r
3639 void DrawSeekClose()\r
3640 {\r
3641 }\r
3642 \r
3643 VOID\r
3644 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3645 {\r
3646   static Board lastReq[2], lastDrawn[2];\r
3647   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3648   static int lastDrawnFlipView = 0;\r
3649   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3650   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3651   HDC tmphdc;\r
3652   HDC hdcmem;\r
3653   HBITMAP bufferBitmap;\r
3654   HBITMAP oldBitmap;\r
3655   RECT Rect;\r
3656   HRGN clips[MAX_CLIPS];\r
3657   ChessSquare dragged_piece = EmptySquare;\r
3658   int nr = twoBoards*partnerUp;\r
3659 \r
3660   /* I'm undecided on this - this function figures out whether a full\r
3661    * repaint is necessary on its own, so there's no real reason to have the\r
3662    * caller tell it that.  I think this can safely be set to FALSE - but\r
3663    * if we trust the callers not to request full repaints unnessesarily, then\r
3664    * we could skip some clipping work.  In other words, only request a full\r
3665    * redraw when the majority of pieces have changed positions (ie. flip, \r
3666    * gamestart and similar)  --Hawk\r
3667    */\r
3668   Boolean fullrepaint = repaint;\r
3669 \r
3670   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3671 \r
3672   if( DrawPositionNeedsFullRepaint() ) {\r
3673       fullrepaint = TRUE;\r
3674   }\r
3675 \r
3676   if (board == NULL) {\r
3677     if (!lastReqValid[nr]) {\r
3678       return;\r
3679     }\r
3680     board = lastReq[nr];\r
3681   } else {\r
3682     CopyBoard(lastReq[nr], board);\r
3683     lastReqValid[nr] = 1;\r
3684   }\r
3685 \r
3686   if (doingSizing) {\r
3687     return;\r
3688   }\r
3689 \r
3690   if (IsIconic(hwndMain)) {\r
3691     return;\r
3692   }\r
3693 \r
3694   if (hdc == NULL) {\r
3695     hdc = GetDC(hwndMain);\r
3696     if (!appData.monoMode) {\r
3697       SelectPalette(hdc, hPal, FALSE);\r
3698       RealizePalette(hdc);\r
3699     }\r
3700     releaseDC = TRUE;\r
3701   } else {\r
3702     releaseDC = FALSE;\r
3703   }\r
3704 \r
3705   /* Create some work-DCs */\r
3706   hdcmem = CreateCompatibleDC(hdc);\r
3707   tmphdc = CreateCompatibleDC(hdc);\r
3708 \r
3709   /* If dragging is in progress, we temporarely remove the piece */\r
3710   /* [HGM] or temporarily decrease count if stacked              */\r
3711   /*       !! Moved to before board compare !!                   */\r
3712   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3713     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3714     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3715             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3716         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3717     } else \r
3718     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3719             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3720         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3721     } else \r
3722         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3723   }\r
3724 \r
3725   /* Figure out which squares need updating by comparing the \r
3726    * newest board with the last drawn board and checking if\r
3727    * flipping has changed.\r
3728    */\r
3729   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3730     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3731       for (column = 0; column < BOARD_WIDTH; column++) {\r
3732         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3733           SquareToPos(row, column, &x, &y);\r
3734           clips[num_clips++] =\r
3735             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3736         }\r
3737       }\r
3738     }\r
3739    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3740     for (i=0; i<2; i++) {\r
3741       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3742           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3743         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3744             lastDrawnHighlight.sq[i].y >= 0) {\r
3745           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3746                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3747           clips[num_clips++] =\r
3748             CreateRectRgn(x - lineGap, y - lineGap, \r
3749                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3750         }\r
3751         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3752           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3753           clips[num_clips++] =\r
3754             CreateRectRgn(x - lineGap, y - lineGap, \r
3755                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3756         }\r
3757       }\r
3758     }\r
3759     for (i=0; i<2; i++) {\r
3760       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3761           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3762         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3763             lastDrawnPremove.sq[i].y >= 0) {\r
3764           SquareToPos(lastDrawnPremove.sq[i].y,\r
3765                       lastDrawnPremove.sq[i].x, &x, &y);\r
3766           clips[num_clips++] =\r
3767             CreateRectRgn(x - lineGap, y - lineGap, \r
3768                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3769         }\r
3770         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3771             premoveHighlightInfo.sq[i].y >= 0) {\r
3772           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3773                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3774           clips[num_clips++] =\r
3775             CreateRectRgn(x - lineGap, y - lineGap, \r
3776                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3777         }\r
3778       }\r
3779     }\r
3780    } else { // nr == 1\r
3781         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3782         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3783         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3784         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3785       for (i=0; i<2; i++) {\r
3786         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3787             partnerHighlightInfo.sq[i].y >= 0) {\r
3788           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3789                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3790           clips[num_clips++] =\r
3791             CreateRectRgn(x - lineGap, y - lineGap, \r
3792                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3793         }\r
3794         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3795             oldPartnerHighlight.sq[i].y >= 0) {\r
3796           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3797                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3798           clips[num_clips++] =\r
3799             CreateRectRgn(x - lineGap, y - lineGap, \r
3800                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3801         }\r
3802       }\r
3803    }\r
3804   } else {\r
3805     fullrepaint = TRUE;\r
3806   }\r
3807 \r
3808   /* Create a buffer bitmap - this is the actual bitmap\r
3809    * being written to.  When all the work is done, we can\r
3810    * copy it to the real DC (the screen).  This avoids\r
3811    * the problems with flickering.\r
3812    */\r
3813   GetClientRect(hwndMain, &Rect);\r
3814   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3815                                         Rect.bottom-Rect.top+1);\r
3816   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3817   if (!appData.monoMode) {\r
3818     SelectPalette(hdcmem, hPal, FALSE);\r
3819   }\r
3820 \r
3821   /* Create clips for dragging */\r
3822   if (!fullrepaint) {\r
3823     if (dragInfo.from.x >= 0) {\r
3824       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3825       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3826     }\r
3827     if (dragInfo.start.x >= 0) {\r
3828       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3829       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3830     }\r
3831     if (dragInfo.pos.x >= 0) {\r
3832       x = dragInfo.pos.x - squareSize / 2;\r
3833       y = dragInfo.pos.y - squareSize / 2;\r
3834       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3835     }\r
3836     if (dragInfo.lastpos.x >= 0) {\r
3837       x = dragInfo.lastpos.x - squareSize / 2;\r
3838       y = dragInfo.lastpos.y - squareSize / 2;\r
3839       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3840     }\r
3841   }\r
3842 \r
3843   /* Are we animating a move?  \r
3844    * If so, \r
3845    *   - remove the piece from the board (temporarely)\r
3846    *   - calculate the clipping region\r
3847    */\r
3848   if (!fullrepaint) {\r
3849     if (animInfo.piece != EmptySquare) {\r
3850       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3851       x = boardRect.left + animInfo.lastpos.x;\r
3852       y = boardRect.top + animInfo.lastpos.y;\r
3853       x2 = boardRect.left + animInfo.pos.x;\r
3854       y2 = boardRect.top + animInfo.pos.y;\r
3855       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3856       /* Slight kludge.  The real problem is that after AnimateMove is\r
3857          done, the position on the screen does not match lastDrawn.\r
3858          This currently causes trouble only on e.p. captures in\r
3859          atomic, where the piece moves to an empty square and then\r
3860          explodes.  The old and new positions both had an empty square\r
3861          at the destination, but animation has drawn a piece there and\r
3862          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3863       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3864     }\r
3865   }\r
3866 \r
3867   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3868   if (num_clips == 0)\r
3869     fullrepaint = TRUE;\r
3870 \r
3871   /* Set clipping on the memory DC */\r
3872   if (!fullrepaint) {\r
3873     SelectClipRgn(hdcmem, clips[0]);\r
3874     for (x = 1; x < num_clips; x++) {\r
3875       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3876         abort();  // this should never ever happen!\r
3877     }\r
3878   }\r
3879 \r
3880   /* Do all the drawing to the memory DC */\r
3881   if(explodeInfo.radius) { // [HGM] atomic\r
3882         HBRUSH oldBrush;\r
3883         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3884         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3885         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3886         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3887         x += squareSize/2;\r
3888         y += squareSize/2;\r
3889         if(!fullrepaint) {\r
3890           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3891           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3892         }\r
3893         DrawGridOnDC(hdcmem);\r
3894         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3895         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3896         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3897         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3898         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3899         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3900         SelectObject(hdcmem, oldBrush);\r
3901   } else {\r
3902     if(border) DrawBackgroundOnDC(hdcmem);\r
3903     DrawGridOnDC(hdcmem);\r
3904     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3905         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3906         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3907     } else {\r
3908         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3909         oldPartnerHighlight = partnerHighlightInfo;\r
3910     }\r
3911     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3912   }\r
3913   if(nr == 0) // [HGM] dual: markers only on left board\r
3914   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3915     for (column = 0; column < BOARD_WIDTH; column++) {\r
3916         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3917             HBRUSH oldBrush = SelectObject(hdcmem, \r
3918                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3919             SquareToPos(row, column, &x, &y);\r
3920             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3921                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3922             SelectObject(hdcmem, oldBrush);\r
3923         }\r
3924     }\r
3925   }\r
3926 \r
3927   if( appData.highlightMoveWithArrow ) {\r
3928     DrawArrowHighlight(hdcmem);\r
3929   }\r
3930 \r
3931   DrawCoordsOnDC(hdcmem);\r
3932 \r
3933   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3934                  /* to make sure lastDrawn contains what is actually drawn */\r
3935 \r
3936   /* Put the dragged piece back into place and draw it (out of place!) */\r
3937     if (dragged_piece != EmptySquare) {\r
3938     /* [HGM] or restack */\r
3939     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3940                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3941     else\r
3942     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3943                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3944     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3945     x = dragInfo.pos.x - squareSize / 2;\r
3946     y = dragInfo.pos.y - squareSize / 2;\r
3947     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3948                   ((int) dragInfo.piece < (int) BlackPawn), \r
3949                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3950   }   \r
3951   \r
3952   /* Put the animated piece back into place and draw it */\r
3953   if (animInfo.piece != EmptySquare) {\r
3954     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3955     x = boardRect.left + animInfo.pos.x;\r
3956     y = boardRect.top + animInfo.pos.y;\r
3957     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3958                   ((int) animInfo.piece < (int) BlackPawn),\r
3959                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3960   }\r
3961 \r
3962   /* Release the bufferBitmap by selecting in the old bitmap \r
3963    * and delete the memory DC\r
3964    */\r
3965   SelectObject(hdcmem, oldBitmap);\r
3966   DeleteDC(hdcmem);\r
3967 \r
3968   /* Set clipping on the target DC */\r
3969   if (!fullrepaint) {\r
3970     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3971         RECT rect;\r
3972         GetRgnBox(clips[x], &rect);\r
3973         DeleteObject(clips[x]);\r
3974         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3975                           rect.right + wpMain.width/2, rect.bottom);\r
3976     }\r
3977     SelectClipRgn(hdc, clips[0]);\r
3978     for (x = 1; x < num_clips; x++) {\r
3979       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3980         abort();   // this should never ever happen!\r
3981     } \r
3982   }\r
3983 \r
3984   /* Copy the new bitmap onto the screen in one go.\r
3985    * This way we avoid any flickering\r
3986    */\r
3987   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3988   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3989          boardRect.right - boardRect.left,\r
3990          boardRect.bottom - boardRect.top,\r
3991          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3992   if(saveDiagFlag) { \r
3993     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3994     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3995 \r
3996     GetObject(bufferBitmap, sizeof(b), &b);\r
3997     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3998         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3999         bih.biWidth = b.bmWidth;\r
4000         bih.biHeight = b.bmHeight;\r
4001         bih.biPlanes = 1;\r
4002         bih.biBitCount = b.bmBitsPixel;\r
4003         bih.biCompression = 0;\r
4004         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4005         bih.biXPelsPerMeter = 0;\r
4006         bih.biYPelsPerMeter = 0;\r
4007         bih.biClrUsed = 0;\r
4008         bih.biClrImportant = 0;\r
4009 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4010 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4011         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4012 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4013 \r
4014         wb = b.bmWidthBytes;\r
4015         // count colors\r
4016         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4017                 int k = ((int*) pData)[i];\r
4018                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4019                 if(j >= 16) break;\r
4020                 color[j] = k;\r
4021                 if(j >= nrColors) nrColors = j+1;\r
4022         }\r
4023         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4024                 INT p = 0;\r
4025                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4026                     for(w=0; w<(wb>>2); w+=2) {\r
4027                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4028                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4029                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4030                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4031                         pData[p++] = m | j<<4;\r
4032                     }\r
4033                     while(p&3) pData[p++] = 0;\r
4034                 }\r
4035                 fac = 3;\r
4036                 wb = ((wb+31)>>5)<<2;\r
4037         }\r
4038         // write BITMAPFILEHEADER\r
4039         fprintf(diagFile, "BM");\r
4040         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4041         fputDW(diagFile, 0);\r
4042         fputDW(diagFile, 0x36 + (fac?64:0));\r
4043         // write BITMAPINFOHEADER\r
4044         fputDW(diagFile, 40);\r
4045         fputDW(diagFile, b.bmWidth);\r
4046         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4047         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4048         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4049         fputDW(diagFile, 0);\r
4050         fputDW(diagFile, 0);\r
4051         fputDW(diagFile, 0);\r
4052         fputDW(diagFile, 0);\r
4053         fputDW(diagFile, 0);\r
4054         fputDW(diagFile, 0);\r
4055         // write color table\r
4056         if(fac)\r
4057         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4058         // write bitmap data\r
4059         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4060                 fputc(pData[i], diagFile);\r
4061         free(pData);\r
4062      }\r
4063   }\r
4064 \r
4065   SelectObject(tmphdc, oldBitmap);\r
4066 \r
4067   /* Massive cleanup */\r
4068   for (x = 0; x < num_clips; x++)\r
4069     DeleteObject(clips[x]);\r
4070 \r
4071   DeleteDC(tmphdc);\r
4072   DeleteObject(bufferBitmap);\r
4073 \r
4074   if (releaseDC) \r
4075     ReleaseDC(hwndMain, hdc);\r
4076   \r
4077   if (lastDrawnFlipView != flipView && nr == 0) {\r
4078     if (flipView)\r
4079       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4080     else\r
4081       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4082   }\r
4083 \r
4084 /*  CopyBoard(lastDrawn, board);*/\r
4085   lastDrawnHighlight = highlightInfo;\r
4086   lastDrawnPremove   = premoveHighlightInfo;\r
4087   lastDrawnFlipView = flipView;\r
4088   lastDrawnValid[nr] = 1;\r
4089 }\r
4090 \r
4091 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4092 int\r
4093 SaveDiagram(f)\r
4094      FILE *f;\r
4095 {\r
4096     saveDiagFlag = 1; diagFile = f;\r
4097     HDCDrawPosition(NULL, TRUE, NULL);\r
4098     saveDiagFlag = 0;\r
4099 \r
4100     fclose(f);\r
4101     return TRUE;\r
4102 }\r
4103 \r
4104 \r
4105 /*---------------------------------------------------------------------------*\\r
4106 | CLIENT PAINT PROCEDURE\r
4107 |   This is the main event-handler for the WM_PAINT message.\r
4108 |\r
4109 \*---------------------------------------------------------------------------*/\r
4110 VOID\r
4111 PaintProc(HWND hwnd)\r
4112 {\r
4113   HDC         hdc;\r
4114   PAINTSTRUCT ps;\r
4115   HFONT       oldFont;\r
4116 \r
4117   if((hdc = BeginPaint(hwnd, &ps))) {\r
4118     if (IsIconic(hwnd)) {\r
4119       DrawIcon(hdc, 2, 2, iconCurrent);\r
4120     } else {\r
4121       if (!appData.monoMode) {\r
4122         SelectPalette(hdc, hPal, FALSE);\r
4123         RealizePalette(hdc);\r
4124       }\r
4125       HDCDrawPosition(hdc, 1, NULL);\r
4126       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4127         flipView = !flipView; partnerUp = !partnerUp;\r
4128         HDCDrawPosition(hdc, 1, NULL);\r
4129         flipView = !flipView; partnerUp = !partnerUp;\r
4130       }\r
4131       oldFont =\r
4132         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4133       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4134                  ETO_CLIPPED|ETO_OPAQUE,\r
4135                  &messageRect, messageText, strlen(messageText), NULL);\r
4136       SelectObject(hdc, oldFont);\r
4137       DisplayBothClocks();\r
4138       DisplayLogos();\r
4139     }\r
4140     EndPaint(hwnd,&ps);\r
4141   }\r
4142 \r
4143   return;\r
4144 }\r
4145 \r
4146 \r
4147 /*\r
4148  * If the user selects on a border boundary, return -1; if off the board,\r
4149  *   return -2.  Otherwise map the event coordinate to the square.\r
4150  * The offset boardRect.left or boardRect.top must already have been\r
4151  *   subtracted from x.\r
4152  */\r
4153 int EventToSquare(x, limit)\r
4154      int x, limit;\r
4155 {\r
4156   if (x <= border)\r
4157     return -2;\r
4158   if (x < lineGap + border)\r
4159     return -1;\r
4160   x -= lineGap + border;\r
4161   if ((x % (squareSize + lineGap)) >= squareSize)\r
4162     return -1;\r
4163   x /= (squareSize + lineGap);\r
4164     if (x >= limit)\r
4165     return -2;\r
4166   return x;\r
4167 }\r
4168 \r
4169 typedef struct {\r
4170   char piece;\r
4171   int command;\r
4172   char* name;\r
4173 } DropEnable;\r
4174 \r
4175 DropEnable dropEnables[] = {\r
4176   { 'P', DP_Pawn, N_("Pawn") },\r
4177   { 'N', DP_Knight, N_("Knight") },\r
4178   { 'B', DP_Bishop, N_("Bishop") },\r
4179   { 'R', DP_Rook, N_("Rook") },\r
4180   { 'Q', DP_Queen, N_("Queen") },\r
4181 };\r
4182 \r
4183 VOID\r
4184 SetupDropMenu(HMENU hmenu)\r
4185 {\r
4186   int i, count, enable;\r
4187   char *p;\r
4188   extern char white_holding[], black_holding[];\r
4189   char item[MSG_SIZ];\r
4190 \r
4191   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4192     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4193                dropEnables[i].piece);\r
4194     count = 0;\r
4195     while (p && *p++ == dropEnables[i].piece) count++;\r
4196       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4197     enable = count > 0 || !appData.testLegality\r
4198       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4199                       && !appData.icsActive);\r
4200     ModifyMenu(hmenu, dropEnables[i].command,\r
4201                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4202                dropEnables[i].command, item);\r
4203   }\r
4204 }\r
4205 \r
4206 void DragPieceBegin(int x, int y, Boolean instantly)\r
4207 {\r
4208       dragInfo.lastpos.x = boardRect.left + x;\r
4209       dragInfo.lastpos.y = boardRect.top + y;\r
4210       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4211       dragInfo.from.x = fromX;\r
4212       dragInfo.from.y = fromY;\r
4213       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4214       dragInfo.start = dragInfo.from;\r
4215       SetCapture(hwndMain);\r
4216 }\r
4217 \r
4218 void DragPieceEnd(int x, int y)\r
4219 {\r
4220     ReleaseCapture();\r
4221     dragInfo.start.x = dragInfo.start.y = -1;\r
4222     dragInfo.from = dragInfo.start;\r
4223     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4224 }\r
4225 \r
4226 void ChangeDragPiece(ChessSquare piece)\r
4227 {\r
4228     dragInfo.piece = piece;\r
4229 }\r
4230 \r
4231 /* Event handler for mouse messages */\r
4232 VOID\r
4233 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4234 {\r
4235   int x, y, menuNr;\r
4236   POINT pt;\r
4237   static int recursive = 0;\r
4238   HMENU hmenu;\r
4239   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4240 \r
4241   if (recursive) {\r
4242     if (message == WM_MBUTTONUP) {\r
4243       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4244          to the middle button: we simulate pressing the left button too!\r
4245          */\r
4246       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4247       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4248     }\r
4249     return;\r
4250   }\r
4251   recursive++;\r
4252   \r
4253   pt.x = LOWORD(lParam);\r
4254   pt.y = HIWORD(lParam);\r
4255   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4256   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4257   if (!flipView && y >= 0) {\r
4258     y = BOARD_HEIGHT - 1 - y;\r
4259   }\r
4260   if (flipView && x >= 0) {\r
4261     x = BOARD_WIDTH - 1 - x;\r
4262   }\r
4263 \r
4264   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4265   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4266 \r
4267   switch (message) {\r
4268   case WM_LBUTTONDOWN:\r
4269       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4270         ClockClick(flipClock); break;\r
4271       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4272         ClockClick(!flipClock); break;\r
4273       }\r
4274       dragInfo.start.x = dragInfo.start.y = -1;\r
4275       dragInfo.from = dragInfo.start;\r
4276     if(fromX == -1 && frozen) { // not sure where this is for\r
4277                 fromX = fromY = -1; \r
4278       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4279       break;\r
4280     }\r
4281       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4282       DrawPosition(TRUE, NULL);\r
4283     break;\r
4284 \r
4285   case WM_LBUTTONUP:\r
4286       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4287       DrawPosition(TRUE, NULL);\r
4288     break;\r
4289 \r
4290   case WM_MOUSEMOVE:\r
4291     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4292     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4293     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4294     if ((appData.animateDragging || appData.highlightDragging)\r
4295         && (wParam & MK_LBUTTON)\r
4296         && dragInfo.from.x >= 0) \r
4297     {\r
4298       BOOL full_repaint = FALSE;\r
4299 \r
4300       if (appData.animateDragging) {\r
4301         dragInfo.pos = pt;\r
4302       }\r
4303       if (appData.highlightDragging) {\r
4304         SetHighlights(fromX, fromY, x, y);\r
4305         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4306             full_repaint = TRUE;\r
4307         }\r
4308       }\r
4309       \r
4310       DrawPosition( full_repaint, NULL);\r
4311       \r
4312       dragInfo.lastpos = dragInfo.pos;\r
4313     }\r
4314     break;\r
4315 \r
4316   case WM_MOUSEWHEEL: // [DM]\r
4317     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4318        /* Mouse Wheel is being rolled forward\r
4319         * Play moves forward\r
4320         */\r
4321        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4322                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4323        /* Mouse Wheel is being rolled backward\r
4324         * Play moves backward\r
4325         */\r
4326        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4327                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4328     }\r
4329     break;\r
4330 \r
4331   case WM_MBUTTONUP:\r
4332   case WM_RBUTTONUP:\r
4333     ReleaseCapture();\r
4334     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4335     break;\r
4336  \r
4337   case WM_MBUTTONDOWN:\r
4338   case WM_RBUTTONDOWN:\r
4339     ErrorPopDown();\r
4340     ReleaseCapture();\r
4341     fromX = fromY = -1;\r
4342     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4343     dragInfo.start.x = dragInfo.start.y = -1;\r
4344     dragInfo.from = dragInfo.start;\r
4345     dragInfo.lastpos = dragInfo.pos;\r
4346     if (appData.highlightDragging) {\r
4347       ClearHighlights();\r
4348     }\r
4349     if(y == -2) {\r
4350       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4351       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4352           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4353       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4354           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4355       }\r
4356       break;\r
4357     }\r
4358     DrawPosition(TRUE, NULL);\r
4359 \r
4360     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4361     switch (menuNr) {\r
4362     case 0:\r
4363       if (message == WM_MBUTTONDOWN) {\r
4364         buttonCount = 3;  /* even if system didn't think so */\r
4365         if (wParam & MK_SHIFT) \r
4366           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4367         else\r
4368           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4369       } else { /* message == WM_RBUTTONDOWN */\r
4370         /* Just have one menu, on the right button.  Windows users don't\r
4371            think to try the middle one, and sometimes other software steals\r
4372            it, or it doesn't really exist. */\r
4373         if(gameInfo.variant != VariantShogi)\r
4374             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4375         else\r
4376             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4377       }\r
4378       break;\r
4379     case 2:\r
4380       SetCapture(hwndMain);\r
4381       break;\r
4382     case 1:\r
4383       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4384       SetupDropMenu(hmenu);\r
4385       MenuPopup(hwnd, pt, hmenu, -1);\r
4386     default:\r
4387       break;\r
4388     }\r
4389     break;\r
4390   }\r
4391 \r
4392   recursive--;\r
4393 }\r
4394 \r
4395 /* Preprocess messages for buttons in main window */\r
4396 LRESULT CALLBACK\r
4397 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4398 {\r
4399   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4400   int i, dir;\r
4401 \r
4402   for (i=0; i<N_BUTTONS; i++) {\r
4403     if (buttonDesc[i].id == id) break;\r
4404   }\r
4405   if (i == N_BUTTONS) return 0;\r
4406   switch (message) {\r
4407   case WM_KEYDOWN:\r
4408     switch (wParam) {\r
4409     case VK_LEFT:\r
4410     case VK_RIGHT:\r
4411       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4412       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4413       return TRUE;\r
4414     }\r
4415     break;\r
4416   case WM_CHAR:\r
4417     switch (wParam) {\r
4418     case '\r':\r
4419       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4420       return TRUE;\r
4421     default:\r
4422       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4423         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4424         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4425         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4426         SetFocus(h);\r
4427         SendMessage(h, WM_CHAR, wParam, lParam);\r
4428         return TRUE;\r
4429       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4430         TypeInEvent((char)wParam);\r
4431       }\r
4432       break;\r
4433     }\r
4434     break;\r
4435   }\r
4436   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4437 }\r
4438 \r
4439 /* Process messages for Promotion dialog box */\r
4440 LRESULT CALLBACK\r
4441 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4442 {\r
4443   char promoChar;\r
4444 \r
4445   switch (message) {\r
4446   case WM_INITDIALOG: /* message: initialize dialog box */\r
4447     /* Center the dialog over the application window */\r
4448     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4449     Translate(hDlg, DLG_PromotionKing);\r
4450     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4451       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4452        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4453        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4454                SW_SHOW : SW_HIDE);\r
4455     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4456     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4457        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4458          PieceToChar(WhiteAngel) != '~') ||\r
4459         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4460          PieceToChar(BlackAngel) != '~')   ) ?\r
4461                SW_SHOW : SW_HIDE);\r
4462     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4463        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4464          PieceToChar(WhiteMarshall) != '~') ||\r
4465         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4466          PieceToChar(BlackMarshall) != '~')   ) ?\r
4467                SW_SHOW : SW_HIDE);\r
4468     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4469     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4470        gameInfo.variant != VariantShogi ?\r
4471                SW_SHOW : SW_HIDE);\r
4472     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4473        gameInfo.variant != VariantShogi ?\r
4474                SW_SHOW : SW_HIDE);\r
4475     if(gameInfo.variant == VariantShogi) {\r
4476         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4477         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4478         SetWindowText(hDlg, "Promote?");\r
4479     }\r
4480     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4481        gameInfo.variant == VariantSuper ?\r
4482                SW_SHOW : SW_HIDE);\r
4483     return TRUE;\r
4484 \r
4485   case WM_COMMAND: /* message: received a command */\r
4486     switch (LOWORD(wParam)) {\r
4487     case IDCANCEL:\r
4488       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4489       ClearHighlights();\r
4490       DrawPosition(FALSE, NULL);\r
4491       return TRUE;\r
4492     case PB_King:\r
4493       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4494       break;\r
4495     case PB_Queen:\r
4496       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4497       break;\r
4498     case PB_Rook:\r
4499       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4500       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4501       break;\r
4502     case PB_Bishop:\r
4503       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4504       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4505       break;\r
4506     case PB_Chancellor:\r
4507       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4508       break;\r
4509     case PB_Archbishop:\r
4510       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4511       break;\r
4512     case PB_Knight:\r
4513       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4514       break;\r
4515     default:\r
4516       return FALSE;\r
4517     }\r
4518     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4519     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4520     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4521     fromX = fromY = -1;\r
4522     if (!appData.highlightLastMove) {\r
4523       ClearHighlights();\r
4524       DrawPosition(FALSE, NULL);\r
4525     }\r
4526     return TRUE;\r
4527   }\r
4528   return FALSE;\r
4529 }\r
4530 \r
4531 /* Pop up promotion dialog */\r
4532 VOID\r
4533 PromotionPopup(HWND hwnd)\r
4534 {\r
4535   FARPROC lpProc;\r
4536 \r
4537   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4538   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4539     hwnd, (DLGPROC)lpProc);\r
4540   FreeProcInstance(lpProc);\r
4541 }\r
4542 \r
4543 void\r
4544 PromotionPopUp()\r
4545 {\r
4546   DrawPosition(TRUE, NULL);\r
4547   PromotionPopup(hwndMain);\r
4548 }\r
4549 \r
4550 VOID\r
4551 LoadGameDialog(HWND hwnd, char* title)\r
4552 {\r
4553   UINT number = 0;\r
4554   FILE *f;\r
4555   char fileTitle[MSG_SIZ];\r
4556   f = OpenFileDialog(hwnd, "rb", "",\r
4557                      appData.oldSaveStyle ? "gam" : "pgn",\r
4558                      GAME_FILT,\r
4559                      title, &number, fileTitle, NULL);\r
4560   if (f != NULL) {\r
4561     cmailMsgLoaded = FALSE;\r
4562     if (number == 0) {\r
4563       int error = GameListBuild(f);\r
4564       if (error) {\r
4565         DisplayError(_("Cannot build game list"), error);\r
4566       } else if (!ListEmpty(&gameList) &&\r
4567                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4568         GameListPopUp(f, fileTitle);\r
4569         return;\r
4570       }\r
4571       GameListDestroy();\r
4572       number = 1;\r
4573     }\r
4574     LoadGame(f, number, fileTitle, FALSE);\r
4575   }\r
4576 }\r
4577 \r
4578 int get_term_width()\r
4579 {\r
4580     HDC hdc;\r
4581     TEXTMETRIC tm;\r
4582     RECT rc;\r
4583     HFONT hfont, hold_font;\r
4584     LOGFONT lf;\r
4585     HWND hText;\r
4586 \r
4587     if (hwndConsole)\r
4588         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4589     else\r
4590         return 79;\r
4591 \r
4592     // get the text metrics\r
4593     hdc = GetDC(hText);\r
4594     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4595     if (consoleCF.dwEffects & CFE_BOLD)\r
4596         lf.lfWeight = FW_BOLD;\r
4597     if (consoleCF.dwEffects & CFE_ITALIC)\r
4598         lf.lfItalic = TRUE;\r
4599     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4600         lf.lfStrikeOut = TRUE;\r
4601     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4602         lf.lfUnderline = TRUE;\r
4603     hfont = CreateFontIndirect(&lf);\r
4604     hold_font = SelectObject(hdc, hfont);\r
4605     GetTextMetrics(hdc, &tm);\r
4606     SelectObject(hdc, hold_font);\r
4607     DeleteObject(hfont);\r
4608     ReleaseDC(hText, hdc);\r
4609 \r
4610     // get the rectangle\r
4611     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4612 \r
4613     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4614 }\r
4615 \r
4616 void UpdateICSWidth(HWND hText)\r
4617 {\r
4618     LONG old_width, new_width;\r
4619 \r
4620     new_width = get_term_width(hText, FALSE);\r
4621     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4622     if (new_width != old_width)\r
4623     {\r
4624         ics_update_width(new_width);\r
4625         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4626     }\r
4627 }\r
4628 \r
4629 VOID\r
4630 ChangedConsoleFont()\r
4631 {\r
4632   CHARFORMAT cfmt;\r
4633   CHARRANGE tmpsel, sel;\r
4634   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4635   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4636   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4637   PARAFORMAT paraf;\r
4638 \r
4639   cfmt.cbSize = sizeof(CHARFORMAT);\r
4640   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4641     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4642                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4643   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4644    * size.  This was undocumented in the version of MSVC++ that I had\r
4645    * when I wrote the code, but is apparently documented now.\r
4646    */\r
4647   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4648   cfmt.bCharSet = f->lf.lfCharSet;\r
4649   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4650   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4651   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4652   /* Why are the following seemingly needed too? */\r
4653   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4654   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4655   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4656   tmpsel.cpMin = 0;\r
4657   tmpsel.cpMax = -1; /*999999?*/\r
4658   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4659   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4660   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4661    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4662    */\r
4663   paraf.cbSize = sizeof(paraf);\r
4664   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4665   paraf.dxStartIndent = 0;\r
4666   paraf.dxOffset = WRAP_INDENT;\r
4667   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4668   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4669   UpdateICSWidth(hText);\r
4670 }\r
4671 \r
4672 /*---------------------------------------------------------------------------*\\r
4673  *\r
4674  * Window Proc for main window\r
4675  *\r
4676 \*---------------------------------------------------------------------------*/\r
4677 \r
4678 /* Process messages for main window, etc. */\r
4679 LRESULT CALLBACK\r
4680 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4681 {\r
4682   FARPROC lpProc;\r
4683   int wmId, wmEvent;\r
4684   char *defName;\r
4685   FILE *f;\r
4686   UINT number;\r
4687   char fileTitle[MSG_SIZ];\r
4688   static SnapData sd;\r
4689   static int peek=0;\r
4690 \r
4691   switch (message) {\r
4692 \r
4693   case WM_PAINT: /* message: repaint portion of window */\r
4694     PaintProc(hwnd);\r
4695     break;\r
4696 \r
4697   case WM_ERASEBKGND:\r
4698     if (IsIconic(hwnd)) {\r
4699       /* Cheat; change the message */\r
4700       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4701     } else {\r
4702       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4703     }\r
4704     break;\r
4705 \r
4706   case WM_LBUTTONDOWN:\r
4707   case WM_MBUTTONDOWN:\r
4708   case WM_RBUTTONDOWN:\r
4709   case WM_LBUTTONUP:\r
4710   case WM_MBUTTONUP:\r
4711   case WM_RBUTTONUP:\r
4712   case WM_MOUSEMOVE:\r
4713   case WM_MOUSEWHEEL:\r
4714     MouseEvent(hwnd, message, wParam, lParam);\r
4715     break;\r
4716 \r
4717   case WM_KEYUP:\r
4718     if((char)wParam == '\b') {\r
4719       ForwardEvent(); peek = 0;\r
4720     }\r
4721 \r
4722     JAWS_KBUP_NAVIGATION\r
4723 \r
4724     break;\r
4725 \r
4726   case WM_KEYDOWN:\r
4727     if((char)wParam == '\b') {\r
4728       if(!peek) BackwardEvent(), peek = 1;\r
4729     }\r
4730 \r
4731     JAWS_KBDOWN_NAVIGATION\r
4732 \r
4733     break;\r
4734 \r
4735   case WM_CHAR:\r
4736     \r
4737     JAWS_ALT_INTERCEPT\r
4738 \r
4739     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4740         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4741         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4742         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4743         SetFocus(h);\r
4744         SendMessage(h, message, wParam, lParam);\r
4745     } else if(lParam != KF_REPEAT) {\r
4746         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4747                 TypeInEvent((char)wParam);\r
4748         } else if((char)wParam == 003) CopyGameToClipboard();\r
4749          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4750     }\r
4751 \r
4752     break;\r
4753 \r
4754   case WM_PALETTECHANGED:\r
4755     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4756       int nnew;\r
4757       HDC hdc = GetDC(hwndMain);\r
4758       SelectPalette(hdc, hPal, TRUE);\r
4759       nnew = RealizePalette(hdc);\r
4760       if (nnew > 0) {\r
4761         paletteChanged = TRUE;\r
4762         InvalidateRect(hwnd, &boardRect, FALSE);\r
4763       }\r
4764       ReleaseDC(hwnd, hdc);\r
4765     }\r
4766     break;\r
4767 \r
4768   case WM_QUERYNEWPALETTE:\r
4769     if (!appData.monoMode /*&& paletteChanged*/) {\r
4770       int nnew;\r
4771       HDC hdc = GetDC(hwndMain);\r
4772       paletteChanged = FALSE;\r
4773       SelectPalette(hdc, hPal, FALSE);\r
4774       nnew = RealizePalette(hdc);\r
4775       if (nnew > 0) {\r
4776         InvalidateRect(hwnd, &boardRect, FALSE);\r
4777       }\r
4778       ReleaseDC(hwnd, hdc);\r
4779       return TRUE;\r
4780     }\r
4781     return FALSE;\r
4782 \r
4783   case WM_COMMAND: /* message: command from application menu */\r
4784     wmId    = LOWORD(wParam);\r
4785     wmEvent = HIWORD(wParam);\r
4786 \r
4787     switch (wmId) {\r
4788     case IDM_NewGame:\r
4789       ResetGameEvent();\r
4790       SAY("new game enter a move to play against the computer with white");\r
4791       break;\r
4792 \r
4793     case IDM_NewGameFRC:\r
4794       if( NewGameFRC() == 0 ) {\r
4795         ResetGameEvent();\r
4796       }\r
4797       break;\r
4798 \r
4799     case IDM_NewVariant:\r
4800       NewVariantPopup(hwnd);\r
4801       break;\r
4802 \r
4803     case IDM_LoadGame:\r
4804       LoadGameDialog(hwnd, _("Load Game from File"));\r
4805       break;\r
4806 \r
4807     case IDM_LoadNextGame:\r
4808       ReloadGame(1);\r
4809       break;\r
4810 \r
4811     case IDM_LoadPrevGame:\r
4812       ReloadGame(-1);\r
4813       break;\r
4814 \r
4815     case IDM_ReloadGame:\r
4816       ReloadGame(0);\r
4817       break;\r
4818 \r
4819     case IDM_LoadPosition:\r
4820       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4821         Reset(FALSE, TRUE);\r
4822       }\r
4823       number = 1;\r
4824       f = OpenFileDialog(hwnd, "rb", "",\r
4825                          appData.oldSaveStyle ? "pos" : "fen",\r
4826                          POSITION_FILT,\r
4827                          _("Load Position from File"), &number, fileTitle, NULL);\r
4828       if (f != NULL) {\r
4829         LoadPosition(f, number, fileTitle);\r
4830       }\r
4831       break;\r
4832 \r
4833     case IDM_LoadNextPosition:\r
4834       ReloadPosition(1);\r
4835       break;\r
4836 \r
4837     case IDM_LoadPrevPosition:\r
4838       ReloadPosition(-1);\r
4839       break;\r
4840 \r
4841     case IDM_ReloadPosition:\r
4842       ReloadPosition(0);\r
4843       break;\r
4844 \r
4845     case IDM_SaveGame:\r
4846       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4847       f = OpenFileDialog(hwnd, "a", defName,\r
4848                          appData.oldSaveStyle ? "gam" : "pgn",\r
4849                          GAME_FILT,\r
4850                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4851       if (f != NULL) {\r
4852         SaveGame(f, 0, "");\r
4853       }\r
4854       break;\r
4855 \r
4856     case IDM_SavePosition:\r
4857       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4858       f = OpenFileDialog(hwnd, "a", defName,\r
4859                          appData.oldSaveStyle ? "pos" : "fen",\r
4860                          POSITION_FILT,\r
4861                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4862       if (f != NULL) {\r
4863         SavePosition(f, 0, "");\r
4864       }\r
4865       break;\r
4866 \r
4867     case IDM_SaveDiagram:\r
4868       defName = "diagram";\r
4869       f = OpenFileDialog(hwnd, "wb", defName,\r
4870                          "bmp",\r
4871                          DIAGRAM_FILT,\r
4872                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4873       if (f != NULL) {\r
4874         SaveDiagram(f);\r
4875       }\r
4876       break;\r
4877 \r
4878     case IDM_CreateBook:\r
4879       CreateBookEvent();\r
4880       break;\r
4881 \r
4882     case IDM_CopyGame:\r
4883       CopyGameToClipboard();\r
4884       break;\r
4885 \r
4886     case IDM_PasteGame:\r
4887       PasteGameFromClipboard();\r
4888       break;\r
4889 \r
4890     case IDM_CopyGameListToClipboard:\r
4891       CopyGameListToClipboard();\r
4892       break;\r
4893 \r
4894     /* [AS] Autodetect FEN or PGN data */\r
4895     case IDM_PasteAny:\r
4896       PasteGameOrFENFromClipboard();\r
4897       break;\r
4898 \r
4899     /* [AS] Move history */\r
4900     case IDM_ShowMoveHistory:\r
4901         if( MoveHistoryIsUp() ) {\r
4902             MoveHistoryPopDown();\r
4903         }\r
4904         else {\r
4905             MoveHistoryPopUp();\r
4906         }\r
4907         break;\r
4908 \r
4909     /* [AS] Eval graph */\r
4910     case IDM_ShowEvalGraph:\r
4911         if( EvalGraphIsUp() ) {\r
4912             EvalGraphPopDown();\r
4913         }\r
4914         else {\r
4915             EvalGraphPopUp();\r
4916             SetFocus(hwndMain);\r
4917         }\r
4918         break;\r
4919 \r
4920     /* [AS] Engine output */\r
4921     case IDM_ShowEngineOutput:\r
4922         if( EngineOutputIsUp() ) {\r
4923             EngineOutputPopDown();\r
4924         }\r
4925         else {\r
4926             EngineOutputPopUp();\r
4927         }\r
4928         break;\r
4929 \r
4930     /* [AS] User adjudication */\r
4931     case IDM_UserAdjudication_White:\r
4932         UserAdjudicationEvent( +1 );\r
4933         break;\r
4934 \r
4935     case IDM_UserAdjudication_Black:\r
4936         UserAdjudicationEvent( -1 );\r
4937         break;\r
4938 \r
4939     case IDM_UserAdjudication_Draw:\r
4940         UserAdjudicationEvent( 0 );\r
4941         break;\r
4942 \r
4943     /* [AS] Game list options dialog */\r
4944     case IDM_GameListOptions:\r
4945       GameListOptions();\r
4946       break;\r
4947 \r
4948     case IDM_NewChat:\r
4949       ChatPopUp(NULL);\r
4950       break;\r
4951 \r
4952     case IDM_CopyPosition:\r
4953       CopyFENToClipboard();\r
4954       break;\r
4955 \r
4956     case IDM_PastePosition:\r
4957       PasteFENFromClipboard();\r
4958       break;\r
4959 \r
4960     case IDM_MailMove:\r
4961       MailMoveEvent();\r
4962       break;\r
4963 \r
4964     case IDM_ReloadCMailMsg:\r
4965       Reset(TRUE, TRUE);\r
4966       ReloadCmailMsgEvent(FALSE);\r
4967       break;\r
4968 \r
4969     case IDM_Minimize:\r
4970       ShowWindow(hwnd, SW_MINIMIZE);\r
4971       break;\r
4972 \r
4973     case IDM_Exit:\r
4974       ExitEvent(0);\r
4975       break;\r
4976 \r
4977     case IDM_MachineWhite:\r
4978       MachineWhiteEvent();\r
4979       /*\r
4980        * refresh the tags dialog only if it's visible\r
4981        */\r
4982       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4983           char *tags;\r
4984           tags = PGNTags(&gameInfo);\r
4985           TagsPopUp(tags, CmailMsg());\r
4986           free(tags);\r
4987       }\r
4988       SAY("computer starts playing white");\r
4989       break;\r
4990 \r
4991     case IDM_MachineBlack:\r
4992       MachineBlackEvent();\r
4993       /*\r
4994        * refresh the tags dialog only if it's visible\r
4995        */\r
4996       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4997           char *tags;\r
4998           tags = PGNTags(&gameInfo);\r
4999           TagsPopUp(tags, CmailMsg());\r
5000           free(tags);\r
5001       }\r
5002       SAY("computer starts playing black");\r
5003       break;\r
5004 \r
5005     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5006       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5007       break;\r
5008 \r
5009     case IDM_TwoMachines:\r
5010       TwoMachinesEvent();\r
5011       /*\r
5012        * refresh the tags dialog only if it's visible\r
5013        */\r
5014       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5015           char *tags;\r
5016           tags = PGNTags(&gameInfo);\r
5017           TagsPopUp(tags, CmailMsg());\r
5018           free(tags);\r
5019       }\r
5020       SAY("computer starts playing both sides");\r
5021       break;\r
5022 \r
5023     case IDM_AnalysisMode:\r
5024       if(AnalyzeModeEvent()) {\r
5025         SAY("analyzing current position");\r
5026       }\r
5027       break;\r
5028 \r
5029     case IDM_AnalyzeFile:\r
5030       AnalyzeFileEvent();\r
5031       break;\r
5032 \r
5033     case IDM_IcsClient:\r
5034       IcsClientEvent();\r
5035       break;\r
5036 \r
5037     case IDM_EditGame:\r
5038     case IDM_EditGame2:\r
5039       EditGameEvent();\r
5040       SAY("edit game");\r
5041       break;\r
5042 \r
5043     case IDM_EditPosition:\r
5044     case IDM_EditPosition2:\r
5045       EditPositionEvent();\r
5046       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5047       break;\r
5048 \r
5049     case IDM_Training:\r
5050       TrainingEvent();\r
5051       break;\r
5052 \r
5053     case IDM_ShowGameList:\r
5054       ShowGameListProc();\r
5055       break;\r
5056 \r
5057     case IDM_EditProgs1:\r
5058       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5059       break;\r
5060 \r
5061     case IDM_LoadProg1:\r
5062      LoadEnginePopUp(hwndMain, 0);\r
5063       break;\r
5064 \r
5065     case IDM_LoadProg2:\r
5066      LoadEnginePopUp(hwndMain, 1);\r
5067       break;\r
5068 \r
5069     case IDM_EditServers:\r
5070       EditTagsPopUp(icsNames, &icsNames);\r
5071       break;\r
5072 \r
5073     case IDM_EditTags:\r
5074     case IDM_Tags:\r
5075       EditTagsProc();\r
5076       break;\r
5077 \r
5078     case IDM_EditBook:\r
5079       EditBookEvent();\r
5080       break;\r
5081 \r
5082     case IDM_EditComment:\r
5083     case IDM_Comment:\r
5084       if (commentUp && editComment) {\r
5085         CommentPopDown();\r
5086       } else {\r
5087         EditCommentEvent();\r
5088       }\r
5089       break;\r
5090 \r
5091     case IDM_Pause:\r
5092       PauseEvent();\r
5093       break;\r
5094 \r
5095     case IDM_Accept:\r
5096       AcceptEvent();\r
5097       break;\r
5098 \r
5099     case IDM_Decline:\r
5100       DeclineEvent();\r
5101       break;\r
5102 \r
5103     case IDM_Rematch:\r
5104       RematchEvent();\r
5105       break;\r
5106 \r
5107     case IDM_CallFlag:\r
5108       CallFlagEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Draw:\r
5112       DrawEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Adjourn:\r
5116       AdjournEvent();\r
5117       break;\r
5118 \r
5119     case IDM_Abort:\r
5120       AbortEvent();\r
5121       break;\r
5122 \r
5123     case IDM_Resign:\r
5124       ResignEvent();\r
5125       break;\r
5126 \r
5127     case IDM_StopObserving:\r
5128       StopObservingEvent();\r
5129       break;\r
5130 \r
5131     case IDM_StopExamining:\r
5132       StopExaminingEvent();\r
5133       break;\r
5134 \r
5135     case IDM_Upload:\r
5136       UploadGameEvent();\r
5137       break;\r
5138 \r
5139     case IDM_TypeInMove:\r
5140       TypeInEvent('\000');\r
5141       break;\r
5142 \r
5143     case IDM_TypeInName:\r
5144       PopUpNameDialog('\000');\r
5145       break;\r
5146 \r
5147     case IDM_Backward:\r
5148       BackwardEvent();\r
5149       SetFocus(hwndMain);\r
5150       break;\r
5151 \r
5152     JAWS_MENU_ITEMS\r
5153 \r
5154     case IDM_Forward:\r
5155       ForwardEvent();\r
5156       SetFocus(hwndMain);\r
5157       break;\r
5158 \r
5159     case IDM_ToStart:\r
5160       ToStartEvent();\r
5161       SetFocus(hwndMain);\r
5162       break;\r
5163 \r
5164     case IDM_ToEnd:\r
5165       ToEndEvent();\r
5166       SetFocus(hwndMain);\r
5167       break;\r
5168 \r
5169     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5170     case OPT_GameListPrev:\r
5171       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5172       break;\r
5173 \r
5174     case IDM_Revert:\r
5175       RevertEvent(FALSE);\r
5176       break;\r
5177 \r
5178     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5179       RevertEvent(TRUE);\r
5180       break;\r
5181 \r
5182     case IDM_TruncateGame:\r
5183       TruncateGameEvent();\r
5184       break;\r
5185 \r
5186     case IDM_MoveNow:\r
5187       MoveNowEvent();\r
5188       break;\r
5189 \r
5190     case IDM_RetractMove:\r
5191       RetractMoveEvent();\r
5192       break;\r
5193 \r
5194     case IDM_FlipView:\r
5195       flipView = !flipView;\r
5196       DrawPosition(FALSE, NULL);\r
5197       break;\r
5198 \r
5199     case IDM_FlipClock:\r
5200       flipClock = !flipClock;\r
5201       DisplayBothClocks();\r
5202       DisplayLogos();\r
5203       break;\r
5204 \r
5205     case IDM_MuteSounds:\r
5206       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5207       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5208                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5209       break;\r
5210 \r
5211     case IDM_GeneralOptions:\r
5212       GeneralOptionsPopup(hwnd);\r
5213       DrawPosition(TRUE, NULL);\r
5214       break;\r
5215 \r
5216     case IDM_BoardOptions:\r
5217       BoardOptionsPopup(hwnd);\r
5218       break;\r
5219 \r
5220     case IDM_ThemeOptions:\r
5221       ThemeOptionsPopup(hwnd);\r
5222       break;\r
5223 \r
5224     case IDM_EnginePlayOptions:\r
5225       EnginePlayOptionsPopup(hwnd);\r
5226       break;\r
5227 \r
5228     case IDM_Engine1Options:\r
5229       EngineOptionsPopup(hwnd, &first);\r
5230       break;\r
5231 \r
5232     case IDM_Engine2Options:\r
5233       savedHwnd = hwnd;\r
5234       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5235       EngineOptionsPopup(hwnd, &second);\r
5236       break;\r
5237 \r
5238     case IDM_OptionsUCI:\r
5239       UciOptionsPopup(hwnd);\r
5240       break;\r
5241 \r
5242     case IDM_Tourney:\r
5243       TourneyPopup(hwnd);\r
5244       break;\r
5245 \r
5246     case IDM_IcsOptions:\r
5247       IcsOptionsPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_Fonts:\r
5251       FontsOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_Sounds:\r
5255       SoundOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_CommPort:\r
5259       CommPortOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_LoadOptions:\r
5263       LoadOptionsPopup(hwnd);\r
5264       break;\r
5265 \r
5266     case IDM_SaveOptions:\r
5267       SaveOptionsPopup(hwnd);\r
5268       break;\r
5269 \r
5270     case IDM_TimeControl:\r
5271       TimeControlOptionsPopup(hwnd);\r
5272       break;\r
5273 \r
5274     case IDM_SaveSettings:\r
5275       SaveSettings(settingsFileName);\r
5276       break;\r
5277 \r
5278     case IDM_SaveSettingsOnExit:\r
5279       saveSettingsOnExit = !saveSettingsOnExit;\r
5280       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5281                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5282                                          MF_CHECKED : MF_UNCHECKED));\r
5283       break;\r
5284 \r
5285     case IDM_Hint:\r
5286       HintEvent();\r
5287       break;\r
5288 \r
5289     case IDM_Book:\r
5290       BookEvent();\r
5291       break;\r
5292 \r
5293     case IDM_AboutGame:\r
5294       AboutGameEvent();\r
5295       break;\r
5296 \r
5297     case IDM_Debug:\r
5298       appData.debugMode = !appData.debugMode;\r
5299       if (appData.debugMode) {\r
5300         char dir[MSG_SIZ];\r
5301         GetCurrentDirectory(MSG_SIZ, dir);\r
5302         SetCurrentDirectory(installDir);\r
5303         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5304         SetCurrentDirectory(dir);\r
5305         setbuf(debugFP, NULL);\r
5306       } else {\r
5307         fclose(debugFP);\r
5308         debugFP = NULL;\r
5309       }\r
5310       break;\r
5311 \r
5312     case IDM_HELPCONTENTS:\r
5313       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5314           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5315           MessageBox (GetFocus(),\r
5316                     _("Unable to activate help"),\r
5317                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5318       }\r
5319       break;\r
5320 \r
5321     case IDM_HELPSEARCH:\r
5322         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5323             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5324         MessageBox (GetFocus(),\r
5325                     _("Unable to activate help"),\r
5326                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5327       }\r
5328       break;\r
5329 \r
5330     case IDM_HELPHELP:\r
5331       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5332         MessageBox (GetFocus(),\r
5333                     _("Unable to activate help"),\r
5334                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5335       }\r
5336       break;\r
5337 \r
5338     case IDM_ABOUT:\r
5339       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5340       DialogBox(hInst, \r
5341         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5342         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5343       FreeProcInstance(lpProc);\r
5344       break;\r
5345 \r
5346     case IDM_DirectCommand1:\r
5347       AskQuestionEvent(_("Direct Command"),\r
5348                        _("Send to chess program:"), "", "1");\r
5349       break;\r
5350     case IDM_DirectCommand2:\r
5351       AskQuestionEvent(_("Direct Command"),\r
5352                        _("Send to second chess program:"), "", "2");\r
5353       break;\r
5354 \r
5355     case EP_WhitePawn:\r
5356       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5357       fromX = fromY = -1;\r
5358       break;\r
5359 \r
5360     case EP_WhiteKnight:\r
5361       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5362       fromX = fromY = -1;\r
5363       break;\r
5364 \r
5365     case EP_WhiteBishop:\r
5366       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5367       fromX = fromY = -1;\r
5368       break;\r
5369 \r
5370     case EP_WhiteRook:\r
5371       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5372       fromX = fromY = -1;\r
5373       break;\r
5374 \r
5375     case EP_WhiteQueen:\r
5376       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5377       fromX = fromY = -1;\r
5378       break;\r
5379 \r
5380     case EP_WhiteFerz:\r
5381       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5382       fromX = fromY = -1;\r
5383       break;\r
5384 \r
5385     case EP_WhiteWazir:\r
5386       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5387       fromX = fromY = -1;\r
5388       break;\r
5389 \r
5390     case EP_WhiteAlfil:\r
5391       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5392       fromX = fromY = -1;\r
5393       break;\r
5394 \r
5395     case EP_WhiteCannon:\r
5396       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case EP_WhiteCardinal:\r
5401       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5402       fromX = fromY = -1;\r
5403       break;\r
5404 \r
5405     case EP_WhiteMarshall:\r
5406       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5407       fromX = fromY = -1;\r
5408       break;\r
5409 \r
5410     case EP_WhiteKing:\r
5411       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5412       fromX = fromY = -1;\r
5413       break;\r
5414 \r
5415     case EP_BlackPawn:\r
5416       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_BlackKnight:\r
5421       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case EP_BlackBishop:\r
5426       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case EP_BlackRook:\r
5431       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case EP_BlackQueen:\r
5436       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_BlackFerz:\r
5441       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_BlackWazir:\r
5446       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_BlackAlfil:\r
5451       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_BlackCannon:\r
5456       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_BlackCardinal:\r
5461       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_BlackMarshall:\r
5466       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_BlackKing:\r
5471       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_EmptySquare:\r
5476       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_ClearBoard:\r
5481       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_White:\r
5486       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_Black:\r
5491       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_Promote:\r
5496       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_Demote:\r
5501       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case DP_Pawn:\r
5506       DropMenuEvent(WhitePawn, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case DP_Knight:\r
5511       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case DP_Bishop:\r
5516       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case DP_Rook:\r
5521       DropMenuEvent(WhiteRook, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case DP_Queen:\r
5526       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case IDM_English:\r
5531       barbaric = 0; appData.language = "";\r
5532       TranslateMenus(0);\r
5533       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5534       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5535       lastChecked = wmId;\r
5536       break;\r
5537 \r
5538     default:\r
5539       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5540           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5541       else\r
5542       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5543           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5544           TranslateMenus(0);\r
5545           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5546           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5547           lastChecked = wmId;\r
5548           break;\r
5549       }\r
5550       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5551     }\r
5552     break;\r
5553 \r
5554   case WM_TIMER:\r
5555     switch (wParam) {\r
5556     case CLOCK_TIMER_ID:\r
5557       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5558       clockTimerEvent = 0;\r
5559       DecrementClocks(); /* call into back end */\r
5560       break;\r
5561     case LOAD_GAME_TIMER_ID:\r
5562       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5563       loadGameTimerEvent = 0;\r
5564       AutoPlayGameLoop(); /* call into back end */\r
5565       break;\r
5566     case ANALYSIS_TIMER_ID:\r
5567       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5568                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5569         AnalysisPeriodicEvent(0);\r
5570       } else {\r
5571         KillTimer(hwnd, analysisTimerEvent);\r
5572         analysisTimerEvent = 0;\r
5573       }\r
5574       break;\r
5575     case DELAYED_TIMER_ID:\r
5576       KillTimer(hwnd, delayedTimerEvent);\r
5577       delayedTimerEvent = 0;\r
5578       delayedTimerCallback();\r
5579       break;\r
5580     }\r
5581     break;\r
5582 \r
5583   case WM_USER_Input:\r
5584     InputEvent(hwnd, message, wParam, lParam);\r
5585     break;\r
5586 \r
5587   /* [AS] Also move "attached" child windows */\r
5588   case WM_WINDOWPOSCHANGING:\r
5589 \r
5590     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5591         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5592 \r
5593         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5594             /* Window is moving */\r
5595             RECT rcMain;\r
5596 \r
5597 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5598             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5599             rcMain.right  = wpMain.x + wpMain.width;\r
5600             rcMain.top    = wpMain.y;\r
5601             rcMain.bottom = wpMain.y + wpMain.height;\r
5602             \r
5603             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5604             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5605             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5606             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5607             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5608             wpMain.x = lpwp->x;\r
5609             wpMain.y = lpwp->y;\r
5610         }\r
5611     }\r
5612     break;\r
5613 \r
5614   /* [AS] Snapping */\r
5615   case WM_ENTERSIZEMOVE:\r
5616     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5617     if (hwnd == hwndMain) {\r
5618       doingSizing = TRUE;\r
5619       lastSizing = 0;\r
5620     }\r
5621     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5622     break;\r
5623 \r
5624   case WM_SIZING:\r
5625     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5626     if (hwnd == hwndMain) {\r
5627       lastSizing = wParam;\r
5628     }\r
5629     break;\r
5630 \r
5631   case WM_MOVING:\r
5632     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5633       return OnMoving( &sd, hwnd, wParam, lParam );\r
5634 \r
5635   case WM_EXITSIZEMOVE:\r
5636     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5637     if (hwnd == hwndMain) {\r
5638       RECT client;\r
5639       doingSizing = FALSE;\r
5640       InvalidateRect(hwnd, &boardRect, FALSE);\r
5641       GetClientRect(hwnd, &client);\r
5642       ResizeBoard(client.right, client.bottom, lastSizing);\r
5643       lastSizing = 0;\r
5644       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5645     }\r
5646     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5647     break;\r
5648 \r
5649   case WM_DESTROY: /* message: window being destroyed */\r
5650     PostQuitMessage(0);\r
5651     break;\r
5652 \r
5653   case WM_CLOSE:\r
5654     if (hwnd == hwndMain) {\r
5655       ExitEvent(0);\r
5656     }\r
5657     break;\r
5658 \r
5659   default:      /* Passes it on if unprocessed */\r
5660     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5661   }\r
5662   return 0;\r
5663 }\r
5664 \r
5665 /*---------------------------------------------------------------------------*\\r
5666  *\r
5667  * Misc utility routines\r
5668  *\r
5669 \*---------------------------------------------------------------------------*/\r
5670 \r
5671 /*\r
5672  * Decent random number generator, at least not as bad as Windows\r
5673  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5674  */\r
5675 unsigned int randstate;\r
5676 \r
5677 int\r
5678 myrandom(void)\r
5679 {\r
5680   randstate = randstate * 1664525 + 1013904223;\r
5681   return (int) randstate & 0x7fffffff;\r
5682 }\r
5683 \r
5684 void\r
5685 mysrandom(unsigned int seed)\r
5686 {\r
5687   randstate = seed;\r
5688 }\r
5689 \r
5690 \r
5691 /* \r
5692  * returns TRUE if user selects a different color, FALSE otherwise \r
5693  */\r
5694 \r
5695 BOOL\r
5696 ChangeColor(HWND hwnd, COLORREF *which)\r
5697 {\r
5698   static BOOL firstTime = TRUE;\r
5699   static DWORD customColors[16];\r
5700   CHOOSECOLOR cc;\r
5701   COLORREF newcolor;\r
5702   int i;\r
5703   ColorClass ccl;\r
5704 \r
5705   if (firstTime) {\r
5706     /* Make initial colors in use available as custom colors */\r
5707     /* Should we put the compiled-in defaults here instead? */\r
5708     i = 0;\r
5709     customColors[i++] = lightSquareColor & 0xffffff;\r
5710     customColors[i++] = darkSquareColor & 0xffffff;\r
5711     customColors[i++] = whitePieceColor & 0xffffff;\r
5712     customColors[i++] = blackPieceColor & 0xffffff;\r
5713     customColors[i++] = highlightSquareColor & 0xffffff;\r
5714     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5715 \r
5716     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5717       customColors[i++] = textAttribs[ccl].color;\r
5718     }\r
5719     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5720     firstTime = FALSE;\r
5721   }\r
5722 \r
5723   cc.lStructSize = sizeof(cc);\r
5724   cc.hwndOwner = hwnd;\r
5725   cc.hInstance = NULL;\r
5726   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5727   cc.lpCustColors = (LPDWORD) customColors;\r
5728   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5729 \r
5730   if (!ChooseColor(&cc)) return FALSE;\r
5731 \r
5732   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5733   if (newcolor == *which) return FALSE;\r
5734   *which = newcolor;\r
5735   return TRUE;\r
5736 \r
5737   /*\r
5738   InitDrawingColors();\r
5739   InvalidateRect(hwnd, &boardRect, FALSE);\r
5740   */\r
5741 }\r
5742 \r
5743 BOOLEAN\r
5744 MyLoadSound(MySound *ms)\r
5745 {\r
5746   BOOL ok = FALSE;\r
5747   struct stat st;\r
5748   FILE *f;\r
5749 \r
5750   if (ms->data && ms->flag) free(ms->data);\r
5751   ms->data = NULL;\r
5752 \r
5753   switch (ms->name[0]) {\r
5754   case NULLCHAR:\r
5755     /* Silence */\r
5756     ok = TRUE;\r
5757     break;\r
5758   case '$':\r
5759     /* System sound from Control Panel.  Don't preload here. */\r
5760     ok = TRUE;\r
5761     break;\r
5762   case '!':\r
5763     if (ms->name[1] == NULLCHAR) {\r
5764       /* "!" alone = silence */\r
5765       ok = TRUE;\r
5766     } else {\r
5767       /* Builtin wave resource.  Error if not found. */\r
5768       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5769       if (h == NULL) break;\r
5770       ms->data = (void *)LoadResource(hInst, h);\r
5771       ms->flag = 0; // not maloced, so cannot be freed!\r
5772       if (h == NULL) break;\r
5773       ok = TRUE;\r
5774     }\r
5775     break;\r
5776   default:\r
5777     /* .wav file.  Error if not found. */\r
5778     f = fopen(ms->name, "rb");\r
5779     if (f == NULL) break;\r
5780     if (fstat(fileno(f), &st) < 0) break;\r
5781     ms->data = malloc(st.st_size);\r
5782     ms->flag = 1;\r
5783     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5784     fclose(f);\r
5785     ok = TRUE;\r
5786     break;\r
5787   }\r
5788   if (!ok) {\r
5789     char buf[MSG_SIZ];\r
5790       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5791     DisplayError(buf, GetLastError());\r
5792   }\r
5793   return ok;\r
5794 }\r
5795 \r
5796 BOOLEAN\r
5797 MyPlaySound(MySound *ms)\r
5798 {\r
5799   BOOLEAN ok = FALSE;\r
5800 \r
5801   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5802   switch (ms->name[0]) {\r
5803   case NULLCHAR:\r
5804         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5805     /* Silence */\r
5806     ok = TRUE;\r
5807     break;\r
5808   case '$':\r
5809     /* System sound from Control Panel (deprecated feature).\r
5810        "$" alone or an unset sound name gets default beep (still in use). */\r
5811     if (ms->name[1]) {\r
5812       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5813     }\r
5814     if (!ok) ok = MessageBeep(MB_OK);\r
5815     break; \r
5816   case '!':\r
5817     /* Builtin wave resource, or "!" alone for silence */\r
5818     if (ms->name[1]) {\r
5819       if (ms->data == NULL) return FALSE;\r
5820       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5821     } else {\r
5822       ok = TRUE;\r
5823     }\r
5824     break;\r
5825   default:\r
5826     /* .wav file.  Error if not found. */\r
5827     if (ms->data == NULL) return FALSE;\r
5828     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5829     break;\r
5830   }\r
5831   /* Don't print an error: this can happen innocently if the sound driver\r
5832      is busy; for instance, if another instance of WinBoard is playing\r
5833      a sound at about the same time. */\r
5834   return ok;\r
5835 }\r
5836 \r
5837 \r
5838 LRESULT CALLBACK\r
5839 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5840 {\r
5841   BOOL ok;\r
5842   OPENFILENAME *ofn;\r
5843   static UINT *number; /* gross that this is static */\r
5844 \r
5845   switch (message) {\r
5846   case WM_INITDIALOG: /* message: initialize dialog box */\r
5847     /* Center the dialog over the application window */\r
5848     ofn = (OPENFILENAME *) lParam;\r
5849     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5850       number = (UINT *) ofn->lCustData;\r
5851       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5852     } else {\r
5853       number = NULL;\r
5854     }\r
5855     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5856     Translate(hDlg, 1536);\r
5857     return FALSE;  /* Allow for further processing */\r
5858 \r
5859   case WM_COMMAND:\r
5860     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5861       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5862     }\r
5863     return FALSE;  /* Allow for further processing */\r
5864   }\r
5865   return FALSE;\r
5866 }\r
5867 \r
5868 UINT APIENTRY\r
5869 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5870 {\r
5871   static UINT *number;\r
5872   OPENFILENAME *ofname;\r
5873   OFNOTIFY *ofnot;\r
5874   switch (uiMsg) {\r
5875   case WM_INITDIALOG:\r
5876     Translate(hdlg, DLG_IndexNumber);\r
5877     ofname = (OPENFILENAME *)lParam;\r
5878     number = (UINT *)(ofname->lCustData);\r
5879     break;\r
5880   case WM_NOTIFY:\r
5881     ofnot = (OFNOTIFY *)lParam;\r
5882     if (ofnot->hdr.code == CDN_FILEOK) {\r
5883       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5884     }\r
5885     break;\r
5886   }\r
5887   return 0;\r
5888 }\r
5889 \r
5890 \r
5891 FILE *\r
5892 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5893                char *nameFilt, char *dlgTitle, UINT *number,\r
5894                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5895 {\r
5896   OPENFILENAME openFileName;\r
5897   char buf1[MSG_SIZ];\r
5898   FILE *f;\r
5899 \r
5900   if (fileName == NULL) fileName = buf1;\r
5901   if (defName == NULL) {\r
5902     safeStrCpy(fileName, "*.", 3 );\r
5903     strcat(fileName, defExt);\r
5904   } else {\r
5905     safeStrCpy(fileName, defName, MSG_SIZ );\r
5906   }\r
5907     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5908   if (number) *number = 0;\r
5909 \r
5910   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5911   openFileName.hwndOwner         = hwnd;\r
5912   openFileName.hInstance         = (HANDLE) hInst;\r
5913   openFileName.lpstrFilter       = nameFilt;\r
5914   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5915   openFileName.nMaxCustFilter    = 0L;\r
5916   openFileName.nFilterIndex      = 1L;\r
5917   openFileName.lpstrFile         = fileName;\r
5918   openFileName.nMaxFile          = MSG_SIZ;\r
5919   openFileName.lpstrFileTitle    = fileTitle;\r
5920   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5921   openFileName.lpstrInitialDir   = NULL;\r
5922   openFileName.lpstrTitle        = dlgTitle;\r
5923   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5924     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5925     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5926     | (oldDialog ? 0 : OFN_EXPLORER);\r
5927   openFileName.nFileOffset       = 0;\r
5928   openFileName.nFileExtension    = 0;\r
5929   openFileName.lpstrDefExt       = defExt;\r
5930   openFileName.lCustData         = (LONG) number;\r
5931   openFileName.lpfnHook          = oldDialog ?\r
5932     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5933   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5934 \r
5935   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5936                         GetOpenFileName(&openFileName)) {\r
5937     /* open the file */\r
5938     f = fopen(openFileName.lpstrFile, write);\r
5939     if (f == NULL) {\r
5940       MessageBox(hwnd, _("File open failed"), NULL,\r
5941                  MB_OK|MB_ICONEXCLAMATION);\r
5942       return NULL;\r
5943     }\r
5944   } else {\r
5945     int err = CommDlgExtendedError();\r
5946     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5947     return FALSE;\r
5948   }\r
5949   return f;\r
5950 }\r
5951 \r
5952 \r
5953 \r
5954 VOID APIENTRY\r
5955 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5956 {\r
5957   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5958 \r
5959   /*\r
5960    * Get the first pop-up menu in the menu template. This is the\r
5961    * menu that TrackPopupMenu displays.\r
5962    */\r
5963   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5964   TranslateOneMenu(10, hmenuTrackPopup);\r
5965 \r
5966   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5967 \r
5968   /*\r
5969    * TrackPopup uses screen coordinates, so convert the\r
5970    * coordinates of the mouse click to screen coordinates.\r
5971    */\r
5972   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5973 \r
5974   /* Draw and track the floating pop-up menu. */\r
5975   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5976                  pt.x, pt.y, 0, hwnd, NULL);\r
5977 \r
5978   /* Destroy the menu.*/\r
5979   DestroyMenu(hmenu);\r
5980 }\r
5981    \r
5982 typedef struct {\r
5983   HWND hDlg, hText;\r
5984   int sizeX, sizeY, newSizeX, newSizeY;\r
5985   HDWP hdwp;\r
5986 } ResizeEditPlusButtonsClosure;\r
5987 \r
5988 BOOL CALLBACK\r
5989 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5990 {\r
5991   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5992   RECT rect;\r
5993   POINT pt;\r
5994 \r
5995   if (hChild == cl->hText) return TRUE;\r
5996   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5997   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5998   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5999   ScreenToClient(cl->hDlg, &pt);\r
6000   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6001     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6002   return TRUE;\r
6003 }\r
6004 \r
6005 /* Resize a dialog that has a (rich) edit field filling most of\r
6006    the top, with a row of buttons below */\r
6007 VOID\r
6008 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6009 {\r
6010   RECT rectText;\r
6011   int newTextHeight, newTextWidth;\r
6012   ResizeEditPlusButtonsClosure cl;\r
6013   \r
6014   /*if (IsIconic(hDlg)) return;*/\r
6015   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6016   \r
6017   cl.hdwp = BeginDeferWindowPos(8);\r
6018 \r
6019   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6020   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6021   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6022   if (newTextHeight < 0) {\r
6023     newSizeY += -newTextHeight;\r
6024     newTextHeight = 0;\r
6025   }\r
6026   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6027     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6028 \r
6029   cl.hDlg = hDlg;\r
6030   cl.hText = hText;\r
6031   cl.sizeX = sizeX;\r
6032   cl.sizeY = sizeY;\r
6033   cl.newSizeX = newSizeX;\r
6034   cl.newSizeY = newSizeY;\r
6035   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6036 \r
6037   EndDeferWindowPos(cl.hdwp);\r
6038 }\r
6039 \r
6040 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6041 {\r
6042     RECT    rChild, rParent;\r
6043     int     wChild, hChild, wParent, hParent;\r
6044     int     wScreen, hScreen, xNew, yNew;\r
6045     HDC     hdc;\r
6046 \r
6047     /* Get the Height and Width of the child window */\r
6048     GetWindowRect (hwndChild, &rChild);\r
6049     wChild = rChild.right - rChild.left;\r
6050     hChild = rChild.bottom - rChild.top;\r
6051 \r
6052     /* Get the Height and Width of the parent window */\r
6053     GetWindowRect (hwndParent, &rParent);\r
6054     wParent = rParent.right - rParent.left;\r
6055     hParent = rParent.bottom - rParent.top;\r
6056 \r
6057     /* Get the display limits */\r
6058     hdc = GetDC (hwndChild);\r
6059     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6060     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6061     ReleaseDC(hwndChild, hdc);\r
6062 \r
6063     /* Calculate new X position, then adjust for screen */\r
6064     xNew = rParent.left + ((wParent - wChild) /2);\r
6065     if (xNew < 0) {\r
6066         xNew = 0;\r
6067     } else if ((xNew+wChild) > wScreen) {\r
6068         xNew = wScreen - wChild;\r
6069     }\r
6070 \r
6071     /* Calculate new Y position, then adjust for screen */\r
6072     if( mode == 0 ) {\r
6073         yNew = rParent.top  + ((hParent - hChild) /2);\r
6074     }\r
6075     else {\r
6076         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6077     }\r
6078 \r
6079     if (yNew < 0) {\r
6080         yNew = 0;\r
6081     } else if ((yNew+hChild) > hScreen) {\r
6082         yNew = hScreen - hChild;\r
6083     }\r
6084 \r
6085     /* Set it, and return */\r
6086     return SetWindowPos (hwndChild, NULL,\r
6087                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6088 }\r
6089 \r
6090 /* Center one window over another */\r
6091 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6092 {\r
6093     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6094 }\r
6095 \r
6096 /*---------------------------------------------------------------------------*\\r
6097  *\r
6098  * Startup Dialog functions\r
6099  *\r
6100 \*---------------------------------------------------------------------------*/\r
6101 void\r
6102 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6103 {\r
6104   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6105 \r
6106   while (*cd != NULL) {\r
6107     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6108     cd++;\r
6109   }\r
6110 }\r
6111 \r
6112 void\r
6113 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6114 {\r
6115   char buf1[MAX_ARG_LEN];\r
6116   int len;\r
6117 \r
6118   if (str[0] == '@') {\r
6119     FILE* f = fopen(str + 1, "r");\r
6120     if (f == NULL) {\r
6121       DisplayFatalError(str + 1, errno, 2);\r
6122       return;\r
6123     }\r
6124     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6125     fclose(f);\r
6126     buf1[len] = NULLCHAR;\r
6127     str = buf1;\r
6128   }\r
6129 \r
6130   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6131 \r
6132   for (;;) {\r
6133     char buf[MSG_SIZ];\r
6134     char *end = strchr(str, '\n');\r
6135     if (end == NULL) return;\r
6136     memcpy(buf, str, end - str);\r
6137     buf[end - str] = NULLCHAR;\r
6138     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6139     str = end + 1;\r
6140   }\r
6141 }\r
6142 \r
6143 void\r
6144 SetStartupDialogEnables(HWND hDlg)\r
6145 {\r
6146   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6147     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6148     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6149   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6150     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6151   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6152     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6153   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6154     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6155   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6156     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6157     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6158     IsDlgButtonChecked(hDlg, OPT_View));\r
6159 }\r
6160 \r
6161 char *\r
6162 QuoteForFilename(char *filename)\r
6163 {\r
6164   int dquote, space;\r
6165   dquote = strchr(filename, '"') != NULL;\r
6166   space = strchr(filename, ' ') != NULL;\r
6167   if (dquote || space) {\r
6168     if (dquote) {\r
6169       return "'";\r
6170     } else {\r
6171       return "\"";\r
6172     }\r
6173   } else {\r
6174     return "";\r
6175   }\r
6176 }\r
6177 \r
6178 VOID\r
6179 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6180 {\r
6181   char buf[MSG_SIZ];\r
6182   char *q;\r
6183 \r
6184   InitComboStringsFromOption(hwndCombo, nthnames);\r
6185   q = QuoteForFilename(nthcp);\r
6186     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6187   if (*nthdir != NULLCHAR) {\r
6188     q = QuoteForFilename(nthdir);\r
6189       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6190   }\r
6191   if (*nthcp == NULLCHAR) {\r
6192     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6193   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6194     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6195     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6196   }\r
6197 }\r
6198 \r
6199 LRESULT CALLBACK\r
6200 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6201 {\r
6202   char buf[MSG_SIZ];\r
6203   HANDLE hwndCombo;\r
6204   char *p;\r
6205 \r
6206   switch (message) {\r
6207   case WM_INITDIALOG:\r
6208     /* Center the dialog */\r
6209     CenterWindow (hDlg, GetDesktopWindow());\r
6210     Translate(hDlg, DLG_Startup);\r
6211     /* Initialize the dialog items */\r
6212     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6213                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6214                   firstChessProgramNames);\r
6215     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6216                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6217                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6218     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6219     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6220       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6221     if (*appData.icsHelper != NULLCHAR) {\r
6222       char *q = QuoteForFilename(appData.icsHelper);\r
6223       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6224     }\r
6225     if (*appData.icsHost == NULLCHAR) {\r
6226       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6227       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6228     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6229       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6230       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6231     }\r
6232 \r
6233     if (appData.icsActive) {\r
6234       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6235     }\r
6236     else if (appData.noChessProgram) {\r
6237       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6238     }\r
6239     else {\r
6240       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6241     }\r
6242 \r
6243     SetStartupDialogEnables(hDlg);\r
6244     return TRUE;\r
6245 \r
6246   case WM_COMMAND:\r
6247     switch (LOWORD(wParam)) {\r
6248     case IDOK:\r
6249       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6250         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6251         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6252         p = buf;\r
6253         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6254         ParseArgs(StringGet, &p);\r
6255         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6256         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6257         p = buf;\r
6258         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6259         ParseArgs(StringGet, &p);\r
6260         SwapEngines(singleList); // ... and then make it 'second'\r
6261         appData.noChessProgram = FALSE;\r
6262         appData.icsActive = FALSE;\r
6263       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6264         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6265         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6266         p = buf;\r
6267         ParseArgs(StringGet, &p);\r
6268         if (appData.zippyPlay) {\r
6269           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6270           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6271           p = buf;\r
6272           ParseArgs(StringGet, &p);\r
6273         }\r
6274       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6275         appData.noChessProgram = TRUE;\r
6276         appData.icsActive = FALSE;\r
6277       } else {\r
6278         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6279                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6280         return TRUE;\r
6281       }\r
6282       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6283         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6284         p = buf;\r
6285         ParseArgs(StringGet, &p);\r
6286       }\r
6287       EndDialog(hDlg, TRUE);\r
6288       return TRUE;\r
6289 \r
6290     case IDCANCEL:\r
6291       ExitEvent(0);\r
6292       return TRUE;\r
6293 \r
6294     case IDM_HELPCONTENTS:\r
6295       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6296         MessageBox (GetFocus(),\r
6297                     _("Unable to activate help"),\r
6298                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6299       }\r
6300       break;\r
6301 \r
6302     default:\r
6303       SetStartupDialogEnables(hDlg);\r
6304       break;\r
6305     }\r
6306     break;\r
6307   }\r
6308   return FALSE;\r
6309 }\r
6310 \r
6311 /*---------------------------------------------------------------------------*\\r
6312  *\r
6313  * About box dialog functions\r
6314  *\r
6315 \*---------------------------------------------------------------------------*/\r
6316 \r
6317 /* Process messages for "About" dialog box */\r
6318 LRESULT CALLBACK\r
6319 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6320 {\r
6321   switch (message) {\r
6322   case WM_INITDIALOG: /* message: initialize dialog box */\r
6323     /* Center the dialog over the application window */\r
6324     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6325     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6326     Translate(hDlg, ABOUTBOX);\r
6327     JAWS_COPYRIGHT\r
6328     return (TRUE);\r
6329 \r
6330   case WM_COMMAND: /* message: received a command */\r
6331     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6332         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6333       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6334       return (TRUE);\r
6335     }\r
6336     break;\r
6337   }\r
6338   return (FALSE);\r
6339 }\r
6340 \r
6341 /*---------------------------------------------------------------------------*\\r
6342  *\r
6343  * Comment Dialog functions\r
6344  *\r
6345 \*---------------------------------------------------------------------------*/\r
6346 \r
6347 LRESULT CALLBACK\r
6348 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6349 {\r
6350   static HANDLE hwndText = NULL;\r
6351   int len, newSizeX, newSizeY, flags;\r
6352   static int sizeX, sizeY;\r
6353   char *str;\r
6354   RECT rect;\r
6355   MINMAXINFO *mmi;\r
6356 \r
6357   switch (message) {\r
6358   case WM_INITDIALOG: /* message: initialize dialog box */\r
6359     /* Initialize the dialog items */\r
6360     Translate(hDlg, DLG_EditComment);\r
6361     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6362     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6363     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6364     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6365     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6366     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6367     SetWindowText(hDlg, commentTitle);\r
6368     if (editComment) {\r
6369       SetFocus(hwndText);\r
6370     } else {\r
6371       SetFocus(GetDlgItem(hDlg, IDOK));\r
6372     }\r
6373     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6374                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6375                 MAKELPARAM(FALSE, 0));\r
6376     /* Size and position the dialog */\r
6377     if (!commentDialog) {\r
6378       commentDialog = hDlg;\r
6379       flags = SWP_NOZORDER;\r
6380       GetClientRect(hDlg, &rect);\r
6381       sizeX = rect.right;\r
6382       sizeY = rect.bottom;\r
6383       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6384           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6385         WINDOWPLACEMENT wp;\r
6386         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6387         wp.length = sizeof(WINDOWPLACEMENT);\r
6388         wp.flags = 0;\r
6389         wp.showCmd = SW_SHOW;\r
6390         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6391         wp.rcNormalPosition.left = wpComment.x;\r
6392         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6393         wp.rcNormalPosition.top = wpComment.y;\r
6394         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6395         SetWindowPlacement(hDlg, &wp);\r
6396 \r
6397         GetClientRect(hDlg, &rect);\r
6398         newSizeX = rect.right;\r
6399         newSizeY = rect.bottom;\r
6400         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6401                               newSizeX, newSizeY);\r
6402         sizeX = newSizeX;\r
6403         sizeY = newSizeY;\r
6404       }\r
6405     }\r
6406     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6407     return FALSE;\r
6408 \r
6409   case WM_COMMAND: /* message: received a command */\r
6410     switch (LOWORD(wParam)) {\r
6411     case IDOK:\r
6412       if (editComment) {\r
6413         char *p, *q;\r
6414         /* Read changed options from the dialog box */\r
6415         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6416         len = GetWindowTextLength(hwndText);\r
6417         str = (char *) malloc(len + 1);\r
6418         GetWindowText(hwndText, str, len + 1);\r
6419         p = q = str;\r
6420         while (*q) {\r
6421           if (*q == '\r')\r
6422             q++;\r
6423           else\r
6424             *p++ = *q++;\r
6425         }\r
6426         *p = NULLCHAR;\r
6427         ReplaceComment(commentIndex, str);\r
6428         free(str);\r
6429       }\r
6430       CommentPopDown();\r
6431       return TRUE;\r
6432 \r
6433     case IDCANCEL:\r
6434     case OPT_CancelComment:\r
6435       CommentPopDown();\r
6436       return TRUE;\r
6437 \r
6438     case OPT_ClearComment:\r
6439       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6440       break;\r
6441 \r
6442     case OPT_EditComment:\r
6443       EditCommentEvent();\r
6444       return TRUE;\r
6445 \r
6446     default:\r
6447       break;\r
6448     }\r
6449     break;\r
6450 \r
6451   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6452         if( wParam == OPT_CommentText ) {\r
6453             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6454 \r
6455             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6456                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6457                 POINTL pt;\r
6458                 LRESULT index;\r
6459 \r
6460                 pt.x = LOWORD( lpMF->lParam );\r
6461                 pt.y = HIWORD( lpMF->lParam );\r
6462 \r
6463                 if(lpMF->msg == WM_CHAR) {\r
6464                         CHARRANGE sel;\r
6465                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6466                         index = sel.cpMin;\r
6467                 } else\r
6468                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6469 \r
6470                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6471                 len = GetWindowTextLength(hwndText);\r
6472                 str = (char *) malloc(len + 1);\r
6473                 GetWindowText(hwndText, str, len + 1);\r
6474                 ReplaceComment(commentIndex, str);\r
6475                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6476                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6477                 free(str);\r
6478 \r
6479                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6480                 lpMF->msg = WM_USER;\r
6481 \r
6482                 return TRUE;\r
6483             }\r
6484         }\r
6485         break;\r
6486 \r
6487   case WM_SIZE:\r
6488     newSizeX = LOWORD(lParam);\r
6489     newSizeY = HIWORD(lParam);\r
6490     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6491     sizeX = newSizeX;\r
6492     sizeY = newSizeY;\r
6493     break;\r
6494 \r
6495   case WM_GETMINMAXINFO:\r
6496     /* Prevent resizing window too small */\r
6497     mmi = (MINMAXINFO *) lParam;\r
6498     mmi->ptMinTrackSize.x = 100;\r
6499     mmi->ptMinTrackSize.y = 100;\r
6500     break;\r
6501   }\r
6502   return FALSE;\r
6503 }\r
6504 \r
6505 VOID\r
6506 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6507 {\r
6508   FARPROC lpProc;\r
6509   char *p, *q;\r
6510 \r
6511   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6512 \r
6513   if (str == NULL) str = "";\r
6514   p = (char *) malloc(2 * strlen(str) + 2);\r
6515   q = p;\r
6516   while (*str) {\r
6517     if (*str == '\n') *q++ = '\r';\r
6518     *q++ = *str++;\r
6519   }\r
6520   *q = NULLCHAR;\r
6521   if (commentText != NULL) free(commentText);\r
6522 \r
6523   commentIndex = index;\r
6524   commentTitle = title;\r
6525   commentText = p;\r
6526   editComment = edit;\r
6527 \r
6528   if (commentDialog) {\r
6529     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6530     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6531   } else {\r
6532     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6533     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6534                  hwndMain, (DLGPROC)lpProc);\r
6535     FreeProcInstance(lpProc);\r
6536   }\r
6537   commentUp = TRUE;\r
6538 }\r
6539 \r
6540 \r
6541 /*---------------------------------------------------------------------------*\\r
6542  *\r
6543  * Type-in move dialog functions\r
6544  * \r
6545 \*---------------------------------------------------------------------------*/\r
6546 \r
6547 LRESULT CALLBACK\r
6548 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6549 {\r
6550   char move[MSG_SIZ];\r
6551   HWND hInput;\r
6552 \r
6553   switch (message) {\r
6554   case WM_INITDIALOG:\r
6555     move[0] = (char) lParam;\r
6556     move[1] = NULLCHAR;\r
6557     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6558     Translate(hDlg, DLG_TypeInMove);\r
6559     hInput = GetDlgItem(hDlg, OPT_Move);\r
6560     SetWindowText(hInput, move);\r
6561     SetFocus(hInput);\r
6562     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6563     return FALSE;\r
6564 \r
6565   case WM_COMMAND:\r
6566     switch (LOWORD(wParam)) {\r
6567     case IDOK:\r
6568 \r
6569       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6570       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6571       TypeInDoneEvent(move);\r
6572       EndDialog(hDlg, TRUE);\r
6573       return TRUE;\r
6574     case IDCANCEL:\r
6575       EndDialog(hDlg, FALSE);\r
6576       return TRUE;\r
6577     default:\r
6578       break;\r
6579     }\r
6580     break;\r
6581   }\r
6582   return FALSE;\r
6583 }\r
6584 \r
6585 VOID\r
6586 PopUpMoveDialog(char firstchar)\r
6587 {\r
6588     FARPROC lpProc;\r
6589 \r
6590       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6591       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6592         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6593       FreeProcInstance(lpProc);\r
6594 }\r
6595 \r
6596 /*---------------------------------------------------------------------------*\\r
6597  *\r
6598  * Type-in name dialog functions\r
6599  * \r
6600 \*---------------------------------------------------------------------------*/\r
6601 \r
6602 LRESULT CALLBACK\r
6603 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6604 {\r
6605   char move[MSG_SIZ];\r
6606   HWND hInput;\r
6607 \r
6608   switch (message) {\r
6609   case WM_INITDIALOG:\r
6610     move[0] = (char) lParam;\r
6611     move[1] = NULLCHAR;\r
6612     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6613     Translate(hDlg, DLG_TypeInName);\r
6614     hInput = GetDlgItem(hDlg, OPT_Name);\r
6615     SetWindowText(hInput, move);\r
6616     SetFocus(hInput);\r
6617     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6618     return FALSE;\r
6619 \r
6620   case WM_COMMAND:\r
6621     switch (LOWORD(wParam)) {\r
6622     case IDOK:\r
6623       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6624       appData.userName = strdup(move);\r
6625       SetUserLogo();\r
6626       SetGameInfo();\r
6627       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6628         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6629         DisplayTitle(move);\r
6630       }\r
6631 \r
6632 \r
6633       EndDialog(hDlg, TRUE);\r
6634       return TRUE;\r
6635     case IDCANCEL:\r
6636       EndDialog(hDlg, FALSE);\r
6637       return TRUE;\r
6638     default:\r
6639       break;\r
6640     }\r
6641     break;\r
6642   }\r
6643   return FALSE;\r
6644 }\r
6645 \r
6646 VOID\r
6647 PopUpNameDialog(char firstchar)\r
6648 {\r
6649     FARPROC lpProc;\r
6650     \r
6651       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6652       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6653         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6654       FreeProcInstance(lpProc);\r
6655 }\r
6656 \r
6657 /*---------------------------------------------------------------------------*\\r
6658  *\r
6659  *  Error dialogs\r
6660  * \r
6661 \*---------------------------------------------------------------------------*/\r
6662 \r
6663 /* Nonmodal error box */\r
6664 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6665                              WPARAM wParam, LPARAM lParam);\r
6666 \r
6667 VOID\r
6668 ErrorPopUp(char *title, char *content)\r
6669 {\r
6670   FARPROC lpProc;\r
6671   char *p, *q;\r
6672   BOOLEAN modal = hwndMain == NULL;\r
6673 \r
6674   p = content;\r
6675   q = errorMessage;\r
6676   while (*p) {\r
6677     if (*p == '\n') {\r
6678       if (modal) {\r
6679         *q++ = ' ';\r
6680         p++;\r
6681       } else {\r
6682         *q++ = '\r';\r
6683         *q++ = *p++;\r
6684       }\r
6685     } else {\r
6686       *q++ = *p++;\r
6687     }\r
6688   }\r
6689   *q = NULLCHAR;\r
6690   strncpy(errorTitle, title, sizeof(errorTitle));\r
6691   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6692   \r
6693   if (modal) {\r
6694     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6695   } else {\r
6696     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6697     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6698                  hwndMain, (DLGPROC)lpProc);\r
6699     FreeProcInstance(lpProc);\r
6700   }\r
6701 }\r
6702 \r
6703 VOID\r
6704 ErrorPopDown()\r
6705 {\r
6706   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6707   if (errorDialog == NULL) return;\r
6708   DestroyWindow(errorDialog);\r
6709   errorDialog = NULL;\r
6710   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6711 }\r
6712 \r
6713 LRESULT CALLBACK\r
6714 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6715 {\r
6716   HANDLE hwndText;\r
6717   RECT rChild;\r
6718 \r
6719   switch (message) {\r
6720   case WM_INITDIALOG:\r
6721     GetWindowRect(hDlg, &rChild);\r
6722 \r
6723     /*\r
6724     SetWindowPos(hDlg, NULL, rChild.left,\r
6725       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6726       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6727     */\r
6728 \r
6729     /* \r
6730         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6731         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6732         and it doesn't work when you resize the dialog.\r
6733         For now, just give it a default position.\r
6734     */\r
6735     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6736     Translate(hDlg, DLG_Error);\r
6737 \r
6738     errorDialog = hDlg;\r
6739     SetWindowText(hDlg, errorTitle);\r
6740     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6741     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6742     return FALSE;\r
6743 \r
6744   case WM_COMMAND:\r
6745     switch (LOWORD(wParam)) {\r
6746     case IDOK:\r
6747     case IDCANCEL:\r
6748       if (errorDialog == hDlg) errorDialog = NULL;\r
6749       DestroyWindow(hDlg);\r
6750       return TRUE;\r
6751 \r
6752     default:\r
6753       break;\r
6754     }\r
6755     break;\r
6756   }\r
6757   return FALSE;\r
6758 }\r
6759 \r
6760 #ifdef GOTHIC\r
6761 HWND gothicDialog = NULL;\r
6762 \r
6763 LRESULT CALLBACK\r
6764 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6765 {\r
6766   HANDLE hwndText;\r
6767   RECT rChild;\r
6768   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6769 \r
6770   switch (message) {\r
6771   case WM_INITDIALOG:\r
6772     GetWindowRect(hDlg, &rChild);\r
6773 \r
6774     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6775                                                              SWP_NOZORDER);\r
6776 \r
6777     /* \r
6778         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6779         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6780         and it doesn't work when you resize the dialog.\r
6781         For now, just give it a default position.\r
6782     */\r
6783     gothicDialog = hDlg;\r
6784     SetWindowText(hDlg, errorTitle);\r
6785     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6786     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6787     return FALSE;\r
6788 \r
6789   case WM_COMMAND:\r
6790     switch (LOWORD(wParam)) {\r
6791     case IDOK:\r
6792     case IDCANCEL:\r
6793       if (errorDialog == hDlg) errorDialog = NULL;\r
6794       DestroyWindow(hDlg);\r
6795       return TRUE;\r
6796 \r
6797     default:\r
6798       break;\r
6799     }\r
6800     break;\r
6801   }\r
6802   return FALSE;\r
6803 }\r
6804 \r
6805 VOID\r
6806 GothicPopUp(char *title, VariantClass variant)\r
6807 {\r
6808   FARPROC lpProc;\r
6809   static char *lastTitle;\r
6810 \r
6811   strncpy(errorTitle, title, sizeof(errorTitle));\r
6812   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6813 \r
6814   if(lastTitle != title && gothicDialog != NULL) {\r
6815     DestroyWindow(gothicDialog);\r
6816     gothicDialog = NULL;\r
6817   }\r
6818   if(variant != VariantNormal && gothicDialog == NULL) {\r
6819     title = lastTitle;\r
6820     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6821     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6822                  hwndMain, (DLGPROC)lpProc);\r
6823     FreeProcInstance(lpProc);\r
6824   }\r
6825 }\r
6826 #endif\r
6827 \r
6828 /*---------------------------------------------------------------------------*\\r
6829  *\r
6830  *  Ics Interaction console functions\r
6831  *\r
6832 \*---------------------------------------------------------------------------*/\r
6833 \r
6834 #define HISTORY_SIZE 64\r
6835 static char *history[HISTORY_SIZE];\r
6836 int histIn = 0, histP = 0;\r
6837 \r
6838 VOID\r
6839 SaveInHistory(char *cmd)\r
6840 {\r
6841   if (history[histIn] != NULL) {\r
6842     free(history[histIn]);\r
6843     history[histIn] = NULL;\r
6844   }\r
6845   if (*cmd == NULLCHAR) return;\r
6846   history[histIn] = StrSave(cmd);\r
6847   histIn = (histIn + 1) % HISTORY_SIZE;\r
6848   if (history[histIn] != NULL) {\r
6849     free(history[histIn]);\r
6850     history[histIn] = NULL;\r
6851   }\r
6852   histP = histIn;\r
6853 }\r
6854 \r
6855 char *\r
6856 PrevInHistory(char *cmd)\r
6857 {\r
6858   int newhp;\r
6859   if (histP == histIn) {\r
6860     if (history[histIn] != NULL) free(history[histIn]);\r
6861     history[histIn] = StrSave(cmd);\r
6862   }\r
6863   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6864   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6865   histP = newhp;\r
6866   return history[histP];\r
6867 }\r
6868 \r
6869 char *\r
6870 NextInHistory()\r
6871 {\r
6872   if (histP == histIn) return NULL;\r
6873   histP = (histP + 1) % HISTORY_SIZE;\r
6874   return history[histP];   \r
6875 }\r
6876 \r
6877 HMENU\r
6878 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6879 {\r
6880   HMENU hmenu, h;\r
6881   int i = 0;\r
6882   hmenu = LoadMenu(hInst, "TextMenu");\r
6883   h = GetSubMenu(hmenu, 0);\r
6884   while (e->item) {\r
6885     if (strcmp(e->item, "-") == 0) {\r
6886       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6887     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6888       int flags = MF_STRING, j = 0;\r
6889       if (e->item[0] == '|') {\r
6890         flags |= MF_MENUBARBREAK;\r
6891         j++;\r
6892       }\r
6893       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6894       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6895     }\r
6896     e++;\r
6897     i++;\r
6898   } \r
6899   return hmenu;\r
6900 }\r
6901 \r
6902 WNDPROC consoleTextWindowProc;\r
6903 \r
6904 void\r
6905 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6906 {\r
6907   char buf[MSG_SIZ], name[MSG_SIZ];\r
6908   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6909   CHARRANGE sel;\r
6910 \r
6911   if (!getname) {\r
6912     SetWindowText(hInput, command);\r
6913     if (immediate) {\r
6914       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6915     } else {\r
6916       sel.cpMin = 999999;\r
6917       sel.cpMax = 999999;\r
6918       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6919       SetFocus(hInput);\r
6920     }\r
6921     return;\r
6922   }    \r
6923   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6924   if (sel.cpMin == sel.cpMax) {\r
6925     /* Expand to surrounding word */\r
6926     TEXTRANGE tr;\r
6927     do {\r
6928       tr.chrg.cpMax = sel.cpMin;\r
6929       tr.chrg.cpMin = --sel.cpMin;\r
6930       if (sel.cpMin < 0) break;\r
6931       tr.lpstrText = name;\r
6932       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6933     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6934     sel.cpMin++;\r
6935 \r
6936     do {\r
6937       tr.chrg.cpMin = sel.cpMax;\r
6938       tr.chrg.cpMax = ++sel.cpMax;\r
6939       tr.lpstrText = name;\r
6940       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6941     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6942     sel.cpMax--;\r
6943 \r
6944     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6945       MessageBeep(MB_ICONEXCLAMATION);\r
6946       return;\r
6947     }\r
6948     tr.chrg = sel;\r
6949     tr.lpstrText = name;\r
6950     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6951   } else {\r
6952     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6953       MessageBeep(MB_ICONEXCLAMATION);\r
6954       return;\r
6955     }\r
6956     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6957   }\r
6958   if (immediate) {\r
6959     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6960     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6961     SetWindowText(hInput, buf);\r
6962     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6963   } else {\r
6964     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6965       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6966     SetWindowText(hInput, buf);\r
6967     sel.cpMin = 999999;\r
6968     sel.cpMax = 999999;\r
6969     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6970     SetFocus(hInput);\r
6971   }\r
6972 }\r
6973 \r
6974 LRESULT CALLBACK \r
6975 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6976 {\r
6977   HWND hInput;\r
6978   CHARRANGE sel;\r
6979 \r
6980   switch (message) {\r
6981   case WM_KEYDOWN:\r
6982     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6983     if(wParam=='R') return 0;\r
6984     switch (wParam) {\r
6985     case VK_PRIOR:\r
6986       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6987       return 0;\r
6988     case VK_NEXT:\r
6989       sel.cpMin = 999999;\r
6990       sel.cpMax = 999999;\r
6991       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6992       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6993       return 0;\r
6994     }\r
6995     break;\r
6996   case WM_CHAR:\r
6997    if(wParam != '\022') {\r
6998     if (wParam == '\t') {\r
6999       if (GetKeyState(VK_SHIFT) < 0) {\r
7000         /* shifted */\r
7001         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7002         if (buttonDesc[0].hwnd) {\r
7003           SetFocus(buttonDesc[0].hwnd);\r
7004         } else {\r
7005           SetFocus(hwndMain);\r
7006         }\r
7007       } else {\r
7008         /* unshifted */\r
7009         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7010       }\r
7011     } else {\r
7012       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7013       JAWS_DELETE( SetFocus(hInput); )\r
7014       SendMessage(hInput, message, wParam, lParam);\r
7015     }\r
7016     return 0;\r
7017    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7018    lParam = -1;\r
7019   case WM_RBUTTONDOWN:\r
7020     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7021       /* Move selection here if it was empty */\r
7022       POINT pt;\r
7023       pt.x = LOWORD(lParam);\r
7024       pt.y = HIWORD(lParam);\r
7025       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7026       if (sel.cpMin == sel.cpMax) {\r
7027         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7028         sel.cpMax = sel.cpMin;\r
7029         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7030       }\r
7031       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7032 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7033       POINT pt;\r
7034       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7035       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7036       if (sel.cpMin == sel.cpMax) {\r
7037         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7038         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7039       }\r
7040       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7041         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7042       }\r
7043       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7044       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7045       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7046       MenuPopup(hwnd, pt, hmenu, -1);\r
7047 }\r
7048     }\r
7049     return 0;\r
7050   case WM_RBUTTONUP:\r
7051     if (GetKeyState(VK_SHIFT) & ~1) {\r
7052       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7053         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7054     }\r
7055     return 0;\r
7056   case WM_PASTE:\r
7057     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7058     SetFocus(hInput);\r
7059     return SendMessage(hInput, message, wParam, lParam);\r
7060   case WM_MBUTTONDOWN:\r
7061     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7062   case WM_COMMAND:\r
7063     switch (LOWORD(wParam)) {\r
7064     case IDM_QuickPaste:\r
7065       {\r
7066         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7067         if (sel.cpMin == sel.cpMax) {\r
7068           MessageBeep(MB_ICONEXCLAMATION);\r
7069           return 0;\r
7070         }\r
7071         SendMessage(hwnd, WM_COPY, 0, 0);\r
7072         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7073         SendMessage(hInput, WM_PASTE, 0, 0);\r
7074         SetFocus(hInput);\r
7075         return 0;\r
7076       }\r
7077     case IDM_Cut:\r
7078       SendMessage(hwnd, WM_CUT, 0, 0);\r
7079       return 0;\r
7080     case IDM_Paste:\r
7081       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7082       return 0;\r
7083     case IDM_Copy:\r
7084       SendMessage(hwnd, WM_COPY, 0, 0);\r
7085       return 0;\r
7086     default:\r
7087       {\r
7088         int i = LOWORD(wParam) - IDM_CommandX;\r
7089         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7090             icsTextMenuEntry[i].command != NULL) {\r
7091           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7092                    icsTextMenuEntry[i].getname,\r
7093                    icsTextMenuEntry[i].immediate);\r
7094           return 0;\r
7095         }\r
7096       }\r
7097       break;\r
7098     }\r
7099     break;\r
7100   }\r
7101   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7102 }\r
7103 \r
7104 WNDPROC consoleInputWindowProc;\r
7105 \r
7106 LRESULT CALLBACK\r
7107 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7108 {\r
7109   char buf[MSG_SIZ];\r
7110   char *p;\r
7111   static BOOL sendNextChar = FALSE;\r
7112   static BOOL quoteNextChar = FALSE;\r
7113   InputSource *is = consoleInputSource;\r
7114   CHARFORMAT cf;\r
7115   CHARRANGE sel;\r
7116 \r
7117   switch (message) {\r
7118   case WM_CHAR:\r
7119     if (!appData.localLineEditing || sendNextChar) {\r
7120       is->buf[0] = (CHAR) wParam;\r
7121       is->count = 1;\r
7122       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7123       sendNextChar = FALSE;\r
7124       return 0;\r
7125     }\r
7126     if (quoteNextChar) {\r
7127       buf[0] = (char) wParam;\r
7128       buf[1] = NULLCHAR;\r
7129       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7130       quoteNextChar = FALSE;\r
7131       return 0;\r
7132     }\r
7133     switch (wParam) {\r
7134     case '\r':   /* Enter key */\r
7135       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7136       if (consoleEcho) SaveInHistory(is->buf);\r
7137       is->buf[is->count++] = '\n';\r
7138       is->buf[is->count] = NULLCHAR;\r
7139       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7140       if (consoleEcho) {\r
7141         ConsoleOutput(is->buf, is->count, TRUE);\r
7142       } else if (appData.localLineEditing) {\r
7143         ConsoleOutput("\n", 1, TRUE);\r
7144       }\r
7145       /* fall thru */\r
7146     case '\033': /* Escape key */\r
7147       SetWindowText(hwnd, "");\r
7148       cf.cbSize = sizeof(CHARFORMAT);\r
7149       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7150       if (consoleEcho) {\r
7151         cf.crTextColor = textAttribs[ColorNormal].color;\r
7152       } else {\r
7153         cf.crTextColor = COLOR_ECHOOFF;\r
7154       }\r
7155       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7156       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7157       return 0;\r
7158     case '\t':   /* Tab key */\r
7159       if (GetKeyState(VK_SHIFT) < 0) {\r
7160         /* shifted */\r
7161         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7162       } else {\r
7163         /* unshifted */\r
7164         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7165         if (buttonDesc[0].hwnd) {\r
7166           SetFocus(buttonDesc[0].hwnd);\r
7167         } else {\r
7168           SetFocus(hwndMain);\r
7169         }\r
7170       }\r
7171       return 0;\r
7172     case '\023': /* Ctrl+S */\r
7173       sendNextChar = TRUE;\r
7174       return 0;\r
7175     case '\021': /* Ctrl+Q */\r
7176       quoteNextChar = TRUE;\r
7177       return 0;\r
7178     JAWS_REPLAY\r
7179     default:\r
7180       break;\r
7181     }\r
7182     break;\r
7183   case WM_KEYDOWN:\r
7184     switch (wParam) {\r
7185     case VK_UP:\r
7186       GetWindowText(hwnd, buf, MSG_SIZ);\r
7187       p = PrevInHistory(buf);\r
7188       if (p != NULL) {\r
7189         SetWindowText(hwnd, p);\r
7190         sel.cpMin = 999999;\r
7191         sel.cpMax = 999999;\r
7192         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7193         return 0;\r
7194       }\r
7195       break;\r
7196     case VK_DOWN:\r
7197       p = NextInHistory();\r
7198       if (p != NULL) {\r
7199         SetWindowText(hwnd, p);\r
7200         sel.cpMin = 999999;\r
7201         sel.cpMax = 999999;\r
7202         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7203         return 0;\r
7204       }\r
7205       break;\r
7206     case VK_HOME:\r
7207     case VK_END:\r
7208       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7209       /* fall thru */\r
7210     case VK_PRIOR:\r
7211     case VK_NEXT:\r
7212       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7213       return 0;\r
7214     }\r
7215     break;\r
7216   case WM_MBUTTONDOWN:\r
7217     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7218       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7219     break;\r
7220   case WM_RBUTTONUP:\r
7221     if (GetKeyState(VK_SHIFT) & ~1) {\r
7222       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7223         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7224     } else {\r
7225       POINT pt;\r
7226       HMENU hmenu;\r
7227       hmenu = LoadMenu(hInst, "InputMenu");\r
7228       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7229       if (sel.cpMin == sel.cpMax) {\r
7230         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7231         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7232       }\r
7233       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7234         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7235       }\r
7236       pt.x = LOWORD(lParam);\r
7237       pt.y = HIWORD(lParam);\r
7238       MenuPopup(hwnd, pt, hmenu, -1);\r
7239     }\r
7240     return 0;\r
7241   case WM_COMMAND:\r
7242     switch (LOWORD(wParam)) { \r
7243     case IDM_Undo:\r
7244       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7245       return 0;\r
7246     case IDM_SelectAll:\r
7247       sel.cpMin = 0;\r
7248       sel.cpMax = -1; /*999999?*/\r
7249       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7250       return 0;\r
7251     case IDM_Cut:\r
7252       SendMessage(hwnd, WM_CUT, 0, 0);\r
7253       return 0;\r
7254     case IDM_Paste:\r
7255       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7256       return 0;\r
7257     case IDM_Copy:\r
7258       SendMessage(hwnd, WM_COPY, 0, 0);\r
7259       return 0;\r
7260     }\r
7261     break;\r
7262   }\r
7263   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7264 }\r
7265 \r
7266 #define CO_MAX  100000\r
7267 #define CO_TRIM   1000\r
7268 \r
7269 LRESULT CALLBACK\r
7270 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7271 {\r
7272   static SnapData sd;\r
7273   HWND hText, hInput;\r
7274   RECT rect;\r
7275   static int sizeX, sizeY;\r
7276   int newSizeX, newSizeY;\r
7277   MINMAXINFO *mmi;\r
7278   WORD wMask;\r
7279 \r
7280   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7281   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7282 \r
7283   switch (message) {\r
7284   case WM_NOTIFY:\r
7285     if (((NMHDR*)lParam)->code == EN_LINK)\r
7286     {\r
7287       ENLINK *pLink = (ENLINK*)lParam;\r
7288       if (pLink->msg == WM_LBUTTONUP)\r
7289       {\r
7290         TEXTRANGE tr;\r
7291 \r
7292         tr.chrg = pLink->chrg;\r
7293         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7294         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7295         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7296         free(tr.lpstrText);\r
7297       }\r
7298     }\r
7299     break;\r
7300   case WM_INITDIALOG: /* message: initialize dialog box */\r
7301     hwndConsole = hDlg;\r
7302     SetFocus(hInput);\r
7303     consoleTextWindowProc = (WNDPROC)\r
7304       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7305     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7306     consoleInputWindowProc = (WNDPROC)\r
7307       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7308     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7309     Colorize(ColorNormal, TRUE);\r
7310     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7311     ChangedConsoleFont();\r
7312     GetClientRect(hDlg, &rect);\r
7313     sizeX = rect.right;\r
7314     sizeY = rect.bottom;\r
7315     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7316         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7317       WINDOWPLACEMENT wp;\r
7318       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7319       wp.length = sizeof(WINDOWPLACEMENT);\r
7320       wp.flags = 0;\r
7321       wp.showCmd = SW_SHOW;\r
7322       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7323       wp.rcNormalPosition.left = wpConsole.x;\r
7324       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7325       wp.rcNormalPosition.top = wpConsole.y;\r
7326       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7327       SetWindowPlacement(hDlg, &wp);\r
7328     }\r
7329 \r
7330    // [HGM] Chessknight's change 2004-07-13\r
7331    else { /* Determine Defaults */\r
7332        WINDOWPLACEMENT wp;\r
7333        wpConsole.x = wpMain.width + 1;\r
7334        wpConsole.y = wpMain.y;\r
7335        wpConsole.width = screenWidth -  wpMain.width;\r
7336        wpConsole.height = wpMain.height;\r
7337        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7338        wp.length = sizeof(WINDOWPLACEMENT);\r
7339        wp.flags = 0;\r
7340        wp.showCmd = SW_SHOW;\r
7341        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7342        wp.rcNormalPosition.left = wpConsole.x;\r
7343        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7344        wp.rcNormalPosition.top = wpConsole.y;\r
7345        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7346        SetWindowPlacement(hDlg, &wp);\r
7347     }\r
7348 \r
7349    // Allow hText to highlight URLs and send notifications on them\r
7350    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7351    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7352    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7353    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7354 \r
7355     return FALSE;\r
7356 \r
7357   case WM_SETFOCUS:\r
7358     SetFocus(hInput);\r
7359     return 0;\r
7360 \r
7361   case WM_CLOSE:\r
7362     ExitEvent(0);\r
7363     /* not reached */\r
7364     break;\r
7365 \r
7366   case WM_SIZE:\r
7367     if (IsIconic(hDlg)) break;\r
7368     newSizeX = LOWORD(lParam);\r
7369     newSizeY = HIWORD(lParam);\r
7370     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7371       RECT rectText, rectInput;\r
7372       POINT pt;\r
7373       int newTextHeight, newTextWidth;\r
7374       GetWindowRect(hText, &rectText);\r
7375       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7376       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7377       if (newTextHeight < 0) {\r
7378         newSizeY += -newTextHeight;\r
7379         newTextHeight = 0;\r
7380       }\r
7381       SetWindowPos(hText, NULL, 0, 0,\r
7382         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7383       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7384       pt.x = rectInput.left;\r
7385       pt.y = rectInput.top + newSizeY - sizeY;\r
7386       ScreenToClient(hDlg, &pt);\r
7387       SetWindowPos(hInput, NULL, \r
7388         pt.x, pt.y, /* needs client coords */   \r
7389         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7390         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7391     }\r
7392     sizeX = newSizeX;\r
7393     sizeY = newSizeY;\r
7394     break;\r
7395 \r
7396   case WM_GETMINMAXINFO:\r
7397     /* Prevent resizing window too small */\r
7398     mmi = (MINMAXINFO *) lParam;\r
7399     mmi->ptMinTrackSize.x = 100;\r
7400     mmi->ptMinTrackSize.y = 100;\r
7401     break;\r
7402 \r
7403   /* [AS] Snapping */\r
7404   case WM_ENTERSIZEMOVE:\r
7405     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7406 \r
7407   case WM_SIZING:\r
7408     return OnSizing( &sd, hDlg, wParam, lParam );\r
7409 \r
7410   case WM_MOVING:\r
7411     return OnMoving( &sd, hDlg, wParam, lParam );\r
7412 \r
7413   case WM_EXITSIZEMOVE:\r
7414         UpdateICSWidth(hText);\r
7415     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7416   }\r
7417 \r
7418   return DefWindowProc(hDlg, message, wParam, lParam);\r
7419 }\r
7420 \r
7421 \r
7422 VOID\r
7423 ConsoleCreate()\r
7424 {\r
7425   HWND hCons;\r
7426   if (hwndConsole) return;\r
7427   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7428   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7429 }\r
7430 \r
7431 \r
7432 VOID\r
7433 ConsoleOutput(char* data, int length, int forceVisible)\r
7434 {\r
7435   HWND hText;\r
7436   int trim, exlen;\r
7437   char *p, *q;\r
7438   char buf[CO_MAX+1];\r
7439   POINT pEnd;\r
7440   RECT rect;\r
7441   static int delayLF = 0;\r
7442   CHARRANGE savesel, sel;\r
7443 \r
7444   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7445   p = data;\r
7446   q = buf;\r
7447   if (delayLF) {\r
7448     *q++ = '\r';\r
7449     *q++ = '\n';\r
7450     delayLF = 0;\r
7451   }\r
7452   while (length--) {\r
7453     if (*p == '\n') {\r
7454       if (*++p) {\r
7455         *q++ = '\r';\r
7456         *q++ = '\n';\r
7457       } else {\r
7458         delayLF = 1;\r
7459       }\r
7460     } else if (*p == '\007') {\r
7461        MyPlaySound(&sounds[(int)SoundBell]);\r
7462        p++;\r
7463     } else {\r
7464       *q++ = *p++;\r
7465     }\r
7466   }\r
7467   *q = NULLCHAR;\r
7468   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7469   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7470   /* Save current selection */\r
7471   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7472   exlen = GetWindowTextLength(hText);\r
7473   /* Find out whether current end of text is visible */\r
7474   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7475   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7476   /* Trim existing text if it's too long */\r
7477   if (exlen + (q - buf) > CO_MAX) {\r
7478     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7479     sel.cpMin = 0;\r
7480     sel.cpMax = trim;\r
7481     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7482     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7483     exlen -= trim;\r
7484     savesel.cpMin -= trim;\r
7485     savesel.cpMax -= trim;\r
7486     if (exlen < 0) exlen = 0;\r
7487     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7488     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7489   }\r
7490   /* Append the new text */\r
7491   sel.cpMin = exlen;\r
7492   sel.cpMax = exlen;\r
7493   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7494   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7495   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7496   if (forceVisible || exlen == 0 ||\r
7497       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7498        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7499     /* Scroll to make new end of text visible if old end of text\r
7500        was visible or new text is an echo of user typein */\r
7501     sel.cpMin = 9999999;\r
7502     sel.cpMax = 9999999;\r
7503     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7504     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7505     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7506     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7507   }\r
7508   if (savesel.cpMax == exlen || forceVisible) {\r
7509     /* Move insert point to new end of text if it was at the old\r
7510        end of text or if the new text is an echo of user typein */\r
7511     sel.cpMin = 9999999;\r
7512     sel.cpMax = 9999999;\r
7513     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7514   } else {\r
7515     /* Restore previous selection */\r
7516     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7517   }\r
7518   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7519 }\r
7520 \r
7521 /*---------*/\r
7522 \r
7523 \r
7524 void\r
7525 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7526 {\r
7527   char buf[100];\r
7528   char *str;\r
7529   COLORREF oldFg, oldBg;\r
7530   HFONT oldFont;\r
7531   RECT rect;\r
7532 \r
7533   if(copyNumber > 1)\r
7534     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7535 \r
7536   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7537   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7538   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7539 \r
7540   rect.left = x;\r
7541   rect.right = x + squareSize;\r
7542   rect.top  = y;\r
7543   rect.bottom = y + squareSize;\r
7544   str = buf;\r
7545 \r
7546   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7547                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7548              y, ETO_CLIPPED|ETO_OPAQUE,\r
7549              &rect, str, strlen(str), NULL);\r
7550 \r
7551   (void) SetTextColor(hdc, oldFg);\r
7552   (void) SetBkColor(hdc, oldBg);\r
7553   (void) SelectObject(hdc, oldFont);\r
7554 }\r
7555 \r
7556 void\r
7557 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7558               RECT *rect, char *color, char *flagFell)\r
7559 {\r
7560   char buf[100];\r
7561   char *str;\r
7562   COLORREF oldFg, oldBg;\r
7563   HFONT oldFont;\r
7564 \r
7565   if (twoBoards && partnerUp) return;\r
7566   if (appData.clockMode) {\r
7567     if (tinyLayout)\r
7568       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7569     else\r
7570       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7571     str = buf;\r
7572   } else {\r
7573     str = color;\r
7574   }\r
7575 \r
7576   if (highlight) {\r
7577     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7578     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7579   } else {\r
7580     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7581     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7582   }\r
7583   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7584 \r
7585   JAWS_SILENCE\r
7586 \r
7587   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7588              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7589              rect, str, strlen(str), NULL);\r
7590   if(logoHeight > 0 && appData.clockMode) {\r
7591       RECT r;\r
7592       str += strlen(color)+2;\r
7593       r.top = rect->top + logoHeight/2;\r
7594       r.left = rect->left;\r
7595       r.right = rect->right;\r
7596       r.bottom = rect->bottom;\r
7597       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7598                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7599                  &r, str, strlen(str), NULL);\r
7600   }\r
7601   (void) SetTextColor(hdc, oldFg);\r
7602   (void) SetBkColor(hdc, oldBg);\r
7603   (void) SelectObject(hdc, oldFont);\r
7604 }\r
7605 \r
7606 \r
7607 int\r
7608 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7609            OVERLAPPED *ovl)\r
7610 {\r
7611   int ok, err;\r
7612 \r
7613   /* [AS]  */\r
7614   if( count <= 0 ) {\r
7615     if (appData.debugMode) {\r
7616       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7617     }\r
7618 \r
7619     return ERROR_INVALID_USER_BUFFER;\r
7620   }\r
7621 \r
7622   ResetEvent(ovl->hEvent);\r
7623   ovl->Offset = ovl->OffsetHigh = 0;\r
7624   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7625   if (ok) {\r
7626     err = NO_ERROR;\r
7627   } else {\r
7628     err = GetLastError();\r
7629     if (err == ERROR_IO_PENDING) {\r
7630       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7631       if (ok)\r
7632         err = NO_ERROR;\r
7633       else\r
7634         err = GetLastError();\r
7635     }\r
7636   }\r
7637   return err;\r
7638 }\r
7639 \r
7640 int\r
7641 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7642             OVERLAPPED *ovl)\r
7643 {\r
7644   int ok, err;\r
7645 \r
7646   ResetEvent(ovl->hEvent);\r
7647   ovl->Offset = ovl->OffsetHigh = 0;\r
7648   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7649   if (ok) {\r
7650     err = NO_ERROR;\r
7651   } else {\r
7652     err = GetLastError();\r
7653     if (err == ERROR_IO_PENDING) {\r
7654       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7655       if (ok)\r
7656         err = NO_ERROR;\r
7657       else\r
7658         err = GetLastError();\r
7659     }\r
7660   }\r
7661   return err;\r
7662 }\r
7663 \r
7664 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7665 void CheckForInputBufferFull( InputSource * is )\r
7666 {\r
7667     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7668         /* Look for end of line */\r
7669         char * p = is->buf;\r
7670         \r
7671         while( p < is->next && *p != '\n' ) {\r
7672             p++;\r
7673         }\r
7674 \r
7675         if( p >= is->next ) {\r
7676             if (appData.debugMode) {\r
7677                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7678             }\r
7679 \r
7680             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7681             is->count = (DWORD) -1;\r
7682             is->next = is->buf;\r
7683         }\r
7684     }\r
7685 }\r
7686 \r
7687 DWORD\r
7688 InputThread(LPVOID arg)\r
7689 {\r
7690   InputSource *is;\r
7691   OVERLAPPED ovl;\r
7692 \r
7693   is = (InputSource *) arg;\r
7694   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7695   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7696   while (is->hThread != NULL) {\r
7697     is->error = DoReadFile(is->hFile, is->next,\r
7698                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7699                            &is->count, &ovl);\r
7700     if (is->error == NO_ERROR) {\r
7701       is->next += is->count;\r
7702     } else {\r
7703       if (is->error == ERROR_BROKEN_PIPE) {\r
7704         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7705         is->count = 0;\r
7706       } else {\r
7707         is->count = (DWORD) -1;\r
7708         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7709         break; \r
7710       }\r
7711     }\r
7712 \r
7713     CheckForInputBufferFull( is );\r
7714 \r
7715     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7716 \r
7717     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7718 \r
7719     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7720   }\r
7721 \r
7722   CloseHandle(ovl.hEvent);\r
7723   CloseHandle(is->hFile);\r
7724 \r
7725   if (appData.debugMode) {\r
7726     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7727   }\r
7728 \r
7729   return 0;\r
7730 }\r
7731 \r
7732 \r
7733 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7734 DWORD\r
7735 NonOvlInputThread(LPVOID arg)\r
7736 {\r
7737   InputSource *is;\r
7738   char *p, *q;\r
7739   int i;\r
7740   char prev;\r
7741 \r
7742   is = (InputSource *) arg;\r
7743   while (is->hThread != NULL) {\r
7744     is->error = ReadFile(is->hFile, is->next,\r
7745                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7746                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7747     if (is->error == NO_ERROR) {\r
7748       /* Change CRLF to LF */\r
7749       if (is->next > is->buf) {\r
7750         p = is->next - 1;\r
7751         i = is->count + 1;\r
7752       } else {\r
7753         p = is->next;\r
7754         i = is->count;\r
7755       }\r
7756       q = p;\r
7757       prev = NULLCHAR;\r
7758       while (i > 0) {\r
7759         if (prev == '\r' && *p == '\n') {\r
7760           *(q-1) = '\n';\r
7761           is->count--;\r
7762         } else { \r
7763           *q++ = *p;\r
7764         }\r
7765         prev = *p++;\r
7766         i--;\r
7767       }\r
7768       *q = NULLCHAR;\r
7769       is->next = q;\r
7770     } else {\r
7771       if (is->error == ERROR_BROKEN_PIPE) {\r
7772         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7773         is->count = 0; \r
7774       } else {\r
7775         is->count = (DWORD) -1;\r
7776       }\r
7777     }\r
7778 \r
7779     CheckForInputBufferFull( is );\r
7780 \r
7781     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7782 \r
7783     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7784 \r
7785     if (is->count < 0) break;  /* Quit on error */\r
7786   }\r
7787   CloseHandle(is->hFile);\r
7788   return 0;\r
7789 }\r
7790 \r
7791 DWORD\r
7792 SocketInputThread(LPVOID arg)\r
7793 {\r
7794   InputSource *is;\r
7795 \r
7796   is = (InputSource *) arg;\r
7797   while (is->hThread != NULL) {\r
7798     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7799     if ((int)is->count == SOCKET_ERROR) {\r
7800       is->count = (DWORD) -1;\r
7801       is->error = WSAGetLastError();\r
7802     } else {\r
7803       is->error = NO_ERROR;\r
7804       is->next += is->count;\r
7805       if (is->count == 0 && is->second == is) {\r
7806         /* End of file on stderr; quit with no message */\r
7807         break;\r
7808       }\r
7809     }\r
7810     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7811 \r
7812     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7813 \r
7814     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7815   }\r
7816   return 0;\r
7817 }\r
7818 \r
7819 VOID\r
7820 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7821 {\r
7822   InputSource *is;\r
7823 \r
7824   is = (InputSource *) lParam;\r
7825   if (is->lineByLine) {\r
7826     /* Feed in lines one by one */\r
7827     char *p = is->buf;\r
7828     char *q = p;\r
7829     while (q < is->next) {\r
7830       if (*q++ == '\n') {\r
7831         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7832         p = q;\r
7833       }\r
7834     }\r
7835     \r
7836     /* Move any partial line to the start of the buffer */\r
7837     q = is->buf;\r
7838     while (p < is->next) {\r
7839       *q++ = *p++;\r
7840     }\r
7841     is->next = q;\r
7842 \r
7843     if (is->error != NO_ERROR || is->count == 0) {\r
7844       /* Notify backend of the error.  Note: If there was a partial\r
7845          line at the end, it is not flushed through. */\r
7846       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7847     }\r
7848   } else {\r
7849     /* Feed in the whole chunk of input at once */\r
7850     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7851     is->next = is->buf;\r
7852   }\r
7853 }\r
7854 \r
7855 /*---------------------------------------------------------------------------*\\r
7856  *\r
7857  *  Menu enables. Used when setting various modes.\r
7858  *\r
7859 \*---------------------------------------------------------------------------*/\r
7860 \r
7861 typedef struct {\r
7862   int item;\r
7863   int flags;\r
7864 } Enables;\r
7865 \r
7866 VOID\r
7867 GreyRevert(Boolean grey)\r
7868 { // [HGM] vari: for retracting variations in local mode\r
7869   HMENU hmenu = GetMenu(hwndMain);\r
7870   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7871   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7872 }\r
7873 \r
7874 VOID\r
7875 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7876 {\r
7877   while (enab->item > 0) {\r
7878     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7879     enab++;\r
7880   }\r
7881 }\r
7882 \r
7883 Enables gnuEnables[] = {\r
7884   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7897 \r
7898   // Needed to switch from ncp to GNU mode on Engine Load\r
7899   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7900   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7913   { -1, -1 }\r
7914 };\r
7915 \r
7916 Enables icsEnables[] = {\r
7917   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7937   { -1, -1 }\r
7938 };\r
7939 \r
7940 #if ZIPPY\r
7941 Enables zippyEnables[] = {\r
7942   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7943   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7946   { -1, -1 }\r
7947 };\r
7948 #endif\r
7949 \r
7950 Enables ncpEnables[] = {\r
7951   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7960   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7961   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7973   { -1, -1 }\r
7974 };\r
7975 \r
7976 Enables trainingOnEnables[] = {\r
7977   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7986   { -1, -1 }\r
7987 };\r
7988 \r
7989 Enables trainingOffEnables[] = {\r
7990   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7995   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7996   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7999   { -1, -1 }\r
8000 };\r
8001 \r
8002 /* These modify either ncpEnables or gnuEnables */\r
8003 Enables cmailEnables[] = {\r
8004   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8006   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8007   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8011   { -1, -1 }\r
8012 };\r
8013 \r
8014 Enables machineThinkingEnables[] = {\r
8015   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8028 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8031   { -1, -1 }\r
8032 };\r
8033 \r
8034 Enables userThinkingEnables[] = {\r
8035   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8048 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8051   { -1, -1 }\r
8052 };\r
8053 \r
8054 /*---------------------------------------------------------------------------*\\r
8055  *\r
8056  *  Front-end interface functions exported by XBoard.\r
8057  *  Functions appear in same order as prototypes in frontend.h.\r
8058  * \r
8059 \*---------------------------------------------------------------------------*/\r
8060 VOID\r
8061 CheckMark(UINT item, int state)\r
8062 {\r
8063     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8064 }\r
8065 \r
8066 VOID\r
8067 ModeHighlight()\r
8068 {\r
8069   static UINT prevChecked = 0;\r
8070   static int prevPausing = 0;\r
8071   UINT nowChecked;\r
8072 \r
8073   if (pausing != prevPausing) {\r
8074     prevPausing = pausing;\r
8075     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8076                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8077     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8078   }\r
8079 \r
8080   switch (gameMode) {\r
8081   case BeginningOfGame:\r
8082     if (appData.icsActive)\r
8083       nowChecked = IDM_IcsClient;\r
8084     else if (appData.noChessProgram)\r
8085       nowChecked = IDM_EditGame;\r
8086     else\r
8087       nowChecked = IDM_MachineBlack;\r
8088     break;\r
8089   case MachinePlaysBlack:\r
8090     nowChecked = IDM_MachineBlack;\r
8091     break;\r
8092   case MachinePlaysWhite:\r
8093     nowChecked = IDM_MachineWhite;\r
8094     break;\r
8095   case TwoMachinesPlay:\r
8096     nowChecked = IDM_TwoMachines;\r
8097     break;\r
8098   case AnalyzeMode:\r
8099     nowChecked = IDM_AnalysisMode;\r
8100     break;\r
8101   case AnalyzeFile:\r
8102     nowChecked = IDM_AnalyzeFile;\r
8103     break;\r
8104   case EditGame:\r
8105     nowChecked = IDM_EditGame;\r
8106     break;\r
8107   case PlayFromGameFile:\r
8108     nowChecked = IDM_LoadGame;\r
8109     break;\r
8110   case EditPosition:\r
8111     nowChecked = IDM_EditPosition;\r
8112     break;\r
8113   case Training:\r
8114     nowChecked = IDM_Training;\r
8115     break;\r
8116   case IcsPlayingWhite:\r
8117   case IcsPlayingBlack:\r
8118   case IcsObserving:\r
8119   case IcsIdle:\r
8120     nowChecked = IDM_IcsClient;\r
8121     break;\r
8122   default:\r
8123   case EndOfGame:\r
8124     nowChecked = 0;\r
8125     break;\r
8126   }\r
8127   CheckMark(prevChecked, MF_UNCHECKED);\r
8128   CheckMark(nowChecked, MF_CHECKED);\r
8129   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8130 \r
8131   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8132     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8133                           MF_BYCOMMAND|MF_ENABLED);\r
8134   } else {\r
8135     (void) EnableMenuItem(GetMenu(hwndMain), \r
8136                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8137   }\r
8138 \r
8139   prevChecked = nowChecked;\r
8140 \r
8141   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8142   if (appData.icsActive) {\r
8143        if (appData.icsEngineAnalyze) {\r
8144                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8145        } else {\r
8146                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8147        }\r
8148   }\r
8149   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8150 }\r
8151 \r
8152 VOID\r
8153 SetICSMode()\r
8154 {\r
8155   HMENU hmenu = GetMenu(hwndMain);\r
8156   SetMenuEnables(hmenu, icsEnables);\r
8157   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8158     MF_BYCOMMAND|MF_ENABLED);\r
8159 #if ZIPPY\r
8160   if (appData.zippyPlay) {\r
8161     SetMenuEnables(hmenu, zippyEnables);\r
8162     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8163          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8164           MF_BYCOMMAND|MF_ENABLED);\r
8165   }\r
8166 #endif\r
8167 }\r
8168 \r
8169 VOID\r
8170 SetGNUMode()\r
8171 {\r
8172   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8173 }\r
8174 \r
8175 VOID\r
8176 SetNCPMode()\r
8177 {\r
8178   HMENU hmenu = GetMenu(hwndMain);\r
8179   SetMenuEnables(hmenu, ncpEnables);\r
8180     DrawMenuBar(hwndMain);\r
8181 }\r
8182 \r
8183 VOID\r
8184 SetCmailMode()\r
8185 {\r
8186   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8187 }\r
8188 \r
8189 VOID \r
8190 SetTrainingModeOn()\r
8191 {\r
8192   int i;\r
8193   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8194   for (i = 0; i < N_BUTTONS; i++) {\r
8195     if (buttonDesc[i].hwnd != NULL)\r
8196       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8197   }\r
8198   CommentPopDown();\r
8199 }\r
8200 \r
8201 VOID SetTrainingModeOff()\r
8202 {\r
8203   int i;\r
8204   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8205   for (i = 0; i < N_BUTTONS; i++) {\r
8206     if (buttonDesc[i].hwnd != NULL)\r
8207       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8208   }\r
8209 }\r
8210 \r
8211 \r
8212 VOID\r
8213 SetUserThinkingEnables()\r
8214 {\r
8215   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8216 }\r
8217 \r
8218 VOID\r
8219 SetMachineThinkingEnables()\r
8220 {\r
8221   HMENU hMenu = GetMenu(hwndMain);\r
8222   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8223 \r
8224   SetMenuEnables(hMenu, machineThinkingEnables);\r
8225 \r
8226   if (gameMode == MachinePlaysBlack) {\r
8227     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8228   } else if (gameMode == MachinePlaysWhite) {\r
8229     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8230   } else if (gameMode == TwoMachinesPlay) {\r
8231     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8232   }\r
8233 }\r
8234 \r
8235 \r
8236 VOID\r
8237 DisplayTitle(char *str)\r
8238 {\r
8239   char title[MSG_SIZ], *host;\r
8240   if (str[0] != NULLCHAR) {\r
8241     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8242   } else if (appData.icsActive) {\r
8243     if (appData.icsCommPort[0] != NULLCHAR)\r
8244       host = "ICS";\r
8245     else \r
8246       host = appData.icsHost;\r
8247       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8248   } else if (appData.noChessProgram) {\r
8249     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8250   } else {\r
8251     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8252     strcat(title, ": ");\r
8253     strcat(title, first.tidy);\r
8254   }\r
8255   SetWindowText(hwndMain, title);\r
8256 }\r
8257 \r
8258 \r
8259 VOID\r
8260 DisplayMessage(char *str1, char *str2)\r
8261 {\r
8262   HDC hdc;\r
8263   HFONT oldFont;\r
8264   int remain = MESSAGE_TEXT_MAX - 1;\r
8265   int len;\r
8266 \r
8267   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8268   messageText[0] = NULLCHAR;\r
8269   if (*str1) {\r
8270     len = strlen(str1);\r
8271     if (len > remain) len = remain;\r
8272     strncpy(messageText, str1, len);\r
8273     messageText[len] = NULLCHAR;\r
8274     remain -= len;\r
8275   }\r
8276   if (*str2 && remain >= 2) {\r
8277     if (*str1) {\r
8278       strcat(messageText, "  ");\r
8279       remain -= 2;\r
8280     }\r
8281     len = strlen(str2);\r
8282     if (len > remain) len = remain;\r
8283     strncat(messageText, str2, len);\r
8284   }\r
8285   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8286   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8287 \r
8288   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8289 \r
8290   SAYMACHINEMOVE();\r
8291 \r
8292   hdc = GetDC(hwndMain);\r
8293   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8294   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8295              &messageRect, messageText, strlen(messageText), NULL);\r
8296   (void) SelectObject(hdc, oldFont);\r
8297   (void) ReleaseDC(hwndMain, hdc);\r
8298 }\r
8299 \r
8300 VOID\r
8301 DisplayError(char *str, int error)\r
8302 {\r
8303   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8304   int len;\r
8305 \r
8306   if (error == 0) {\r
8307     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8308   } else {\r
8309     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8310                         NULL, error, LANG_NEUTRAL,\r
8311                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8312     if (len > 0) {\r
8313       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8314     } else {\r
8315       ErrorMap *em = errmap;\r
8316       while (em->err != 0 && em->err != error) em++;\r
8317       if (em->err != 0) {\r
8318         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8319       } else {\r
8320         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8321       }\r
8322     }\r
8323   }\r
8324   \r
8325   ErrorPopUp(_("Error"), buf);\r
8326 }\r
8327 \r
8328 \r
8329 VOID\r
8330 DisplayMoveError(char *str)\r
8331 {\r
8332   fromX = fromY = -1;\r
8333   ClearHighlights();\r
8334   DrawPosition(FALSE, NULL);\r
8335   if (appData.popupMoveErrors) {\r
8336     ErrorPopUp(_("Error"), str);\r
8337   } else {\r
8338     DisplayMessage(str, "");\r
8339     moveErrorMessageUp = TRUE;\r
8340   }\r
8341 }\r
8342 \r
8343 VOID\r
8344 DisplayFatalError(char *str, int error, int exitStatus)\r
8345 {\r
8346   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8347   int len;\r
8348   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8349 \r
8350   if (error != 0) {\r
8351     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8352                         NULL, error, LANG_NEUTRAL,\r
8353                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8354     if (len > 0) {\r
8355       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8356     } else {\r
8357       ErrorMap *em = errmap;\r
8358       while (em->err != 0 && em->err != error) em++;\r
8359       if (em->err != 0) {\r
8360         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8361       } else {\r
8362         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8363       }\r
8364     }\r
8365     str = buf;\r
8366   }\r
8367   if (appData.debugMode) {\r
8368     fprintf(debugFP, "%s: %s\n", label, str);\r
8369   }\r
8370   if (appData.popupExitMessage) {\r
8371     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8372                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8373   }\r
8374   ExitEvent(exitStatus);\r
8375 }\r
8376 \r
8377 \r
8378 VOID\r
8379 DisplayInformation(char *str)\r
8380 {\r
8381   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8382 }\r
8383 \r
8384 \r
8385 VOID\r
8386 DisplayNote(char *str)\r
8387 {\r
8388   ErrorPopUp(_("Note"), str);\r
8389 }\r
8390 \r
8391 \r
8392 typedef struct {\r
8393   char *title, *question, *replyPrefix;\r
8394   ProcRef pr;\r
8395 } QuestionParams;\r
8396 \r
8397 LRESULT CALLBACK\r
8398 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8399 {\r
8400   static QuestionParams *qp;\r
8401   char reply[MSG_SIZ];\r
8402   int len, err;\r
8403 \r
8404   switch (message) {\r
8405   case WM_INITDIALOG:\r
8406     qp = (QuestionParams *) lParam;\r
8407     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8408     Translate(hDlg, DLG_Question);\r
8409     SetWindowText(hDlg, qp->title);\r
8410     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8411     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8412     return FALSE;\r
8413 \r
8414   case WM_COMMAND:\r
8415     switch (LOWORD(wParam)) {\r
8416     case IDOK:\r
8417       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8418       if (*reply) strcat(reply, " ");\r
8419       len = strlen(reply);\r
8420       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8421       strcat(reply, "\n");\r
8422       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8423       EndDialog(hDlg, TRUE);\r
8424       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8425       return TRUE;\r
8426     case IDCANCEL:\r
8427       EndDialog(hDlg, FALSE);\r
8428       return TRUE;\r
8429     default:\r
8430       break;\r
8431     }\r
8432     break;\r
8433   }\r
8434   return FALSE;\r
8435 }\r
8436 \r
8437 VOID\r
8438 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8439 {\r
8440     QuestionParams qp;\r
8441     FARPROC lpProc;\r
8442     \r
8443     qp.title = title;\r
8444     qp.question = question;\r
8445     qp.replyPrefix = replyPrefix;\r
8446     qp.pr = pr;\r
8447     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8448     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8449       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8450     FreeProcInstance(lpProc);\r
8451 }\r
8452 \r
8453 /* [AS] Pick FRC position */\r
8454 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8455 {\r
8456     static int * lpIndexFRC;\r
8457     BOOL index_is_ok;\r
8458     char buf[16];\r
8459 \r
8460     switch( message )\r
8461     {\r
8462     case WM_INITDIALOG:\r
8463         lpIndexFRC = (int *) lParam;\r
8464 \r
8465         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8466         Translate(hDlg, DLG_NewGameFRC);\r
8467 \r
8468         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8469         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8470         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8471         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8472 \r
8473         break;\r
8474 \r
8475     case WM_COMMAND:\r
8476         switch( LOWORD(wParam) ) {\r
8477         case IDOK:\r
8478             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8479             EndDialog( hDlg, 0 );\r
8480             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8481             return TRUE;\r
8482         case IDCANCEL:\r
8483             EndDialog( hDlg, 1 );   \r
8484             return TRUE;\r
8485         case IDC_NFG_Edit:\r
8486             if( HIWORD(wParam) == EN_CHANGE ) {\r
8487                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8488 \r
8489                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8490             }\r
8491             return TRUE;\r
8492         case IDC_NFG_Random:\r
8493           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8494             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8495             return TRUE;\r
8496         }\r
8497 \r
8498         break;\r
8499     }\r
8500 \r
8501     return FALSE;\r
8502 }\r
8503 \r
8504 int NewGameFRC()\r
8505 {\r
8506     int result;\r
8507     int index = appData.defaultFrcPosition;\r
8508     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8509 \r
8510     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8511 \r
8512     if( result == 0 ) {\r
8513         appData.defaultFrcPosition = index;\r
8514     }\r
8515 \r
8516     return result;\r
8517 }\r
8518 \r
8519 /* [AS] Game list options. Refactored by HGM */\r
8520 \r
8521 HWND gameListOptionsDialog;\r
8522 \r
8523 // low-level front-end: clear text edit / list widget\r
8524 void\r
8525 GLT_ClearList()\r
8526 {\r
8527     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8528 }\r
8529 \r
8530 // low-level front-end: clear text edit / list widget\r
8531 void\r
8532 GLT_DeSelectList()\r
8533 {\r
8534     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8535 }\r
8536 \r
8537 // low-level front-end: append line to text edit / list widget\r
8538 void\r
8539 GLT_AddToList( char *name )\r
8540 {\r
8541     if( name != 0 ) {\r
8542             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8543     }\r
8544 }\r
8545 \r
8546 // low-level front-end: get line from text edit / list widget\r
8547 Boolean\r
8548 GLT_GetFromList( int index, char *name )\r
8549 {\r
8550     if( name != 0 ) {\r
8551             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8552                 return TRUE;\r
8553     }\r
8554     return FALSE;\r
8555 }\r
8556 \r
8557 void GLT_MoveSelection( HWND hDlg, int delta )\r
8558 {\r
8559     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8560     int idx2 = idx1 + delta;\r
8561     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8562 \r
8563     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8564         char buf[128];\r
8565 \r
8566         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8567         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8568         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8569         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8570     }\r
8571 }\r
8572 \r
8573 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8574 {\r
8575     switch( message )\r
8576     {\r
8577     case WM_INITDIALOG:\r
8578         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8579         \r
8580         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8581         Translate(hDlg, DLG_GameListOptions);\r
8582 \r
8583         /* Initialize list */\r
8584         GLT_TagsToList( lpUserGLT );\r
8585 \r
8586         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8587 \r
8588         break;\r
8589 \r
8590     case WM_COMMAND:\r
8591         switch( LOWORD(wParam) ) {\r
8592         case IDOK:\r
8593             GLT_ParseList();\r
8594             EndDialog( hDlg, 0 );\r
8595             return TRUE;\r
8596         case IDCANCEL:\r
8597             EndDialog( hDlg, 1 );\r
8598             return TRUE;\r
8599 \r
8600         case IDC_GLT_Default:\r
8601             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8602             return TRUE;\r
8603 \r
8604         case IDC_GLT_Restore:\r
8605             GLT_TagsToList( appData.gameListTags );\r
8606             return TRUE;\r
8607 \r
8608         case IDC_GLT_Up:\r
8609             GLT_MoveSelection( hDlg, -1 );\r
8610             return TRUE;\r
8611 \r
8612         case IDC_GLT_Down:\r
8613             GLT_MoveSelection( hDlg, +1 );\r
8614             return TRUE;\r
8615         }\r
8616 \r
8617         break;\r
8618     }\r
8619 \r
8620     return FALSE;\r
8621 }\r
8622 \r
8623 int GameListOptions()\r
8624 {\r
8625     int result;\r
8626     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8627 \r
8628       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8629 \r
8630     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8631 \r
8632     if( result == 0 ) {\r
8633         /* [AS] Memory leak here! */\r
8634         appData.gameListTags = strdup( lpUserGLT ); \r
8635     }\r
8636 \r
8637     return result;\r
8638 }\r
8639 \r
8640 VOID\r
8641 DisplayIcsInteractionTitle(char *str)\r
8642 {\r
8643   char consoleTitle[MSG_SIZ];\r
8644 \r
8645     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8646     SetWindowText(hwndConsole, consoleTitle);\r
8647 \r
8648     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8649       char buf[MSG_SIZ], *p = buf, *q;\r
8650         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8651       do {\r
8652         q = strchr(p, ';');\r
8653         if(q) *q++ = 0;\r
8654         if(*p) ChatPopUp(p);\r
8655       } while(p=q);\r
8656     }\r
8657 \r
8658     SetActiveWindow(hwndMain);\r
8659 }\r
8660 \r
8661 void\r
8662 DrawPosition(int fullRedraw, Board board)\r
8663 {\r
8664   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8665 }\r
8666 \r
8667 void NotifyFrontendLogin()\r
8668 {\r
8669         if (hwndConsole)\r
8670                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8671 }\r
8672 \r
8673 VOID\r
8674 ResetFrontEnd()\r
8675 {\r
8676   fromX = fromY = -1;\r
8677   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8678     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8679     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8680     dragInfo.lastpos = dragInfo.pos;\r
8681     dragInfo.start.x = dragInfo.start.y = -1;\r
8682     dragInfo.from = dragInfo.start;\r
8683     ReleaseCapture();\r
8684     DrawPosition(TRUE, NULL);\r
8685   }\r
8686   TagsPopDown();\r
8687 }\r
8688 \r
8689 \r
8690 VOID\r
8691 CommentPopUp(char *title, char *str)\r
8692 {\r
8693   HWND hwnd = GetActiveWindow();\r
8694   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8695   SAY(str);\r
8696   SetActiveWindow(hwnd);\r
8697 }\r
8698 \r
8699 VOID\r
8700 CommentPopDown(void)\r
8701 {\r
8702   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8703   if (commentDialog) {\r
8704     ShowWindow(commentDialog, SW_HIDE);\r
8705   }\r
8706   commentUp = FALSE;\r
8707 }\r
8708 \r
8709 VOID\r
8710 EditCommentPopUp(int index, char *title, char *str)\r
8711 {\r
8712   EitherCommentPopUp(index, title, str, TRUE);\r
8713 }\r
8714 \r
8715 \r
8716 VOID\r
8717 RingBell()\r
8718 {\r
8719   MyPlaySound(&sounds[(int)SoundMove]);\r
8720 }\r
8721 \r
8722 VOID PlayIcsWinSound()\r
8723 {\r
8724   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8725 }\r
8726 \r
8727 VOID PlayIcsLossSound()\r
8728 {\r
8729   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8730 }\r
8731 \r
8732 VOID PlayIcsDrawSound()\r
8733 {\r
8734   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8735 }\r
8736 \r
8737 VOID PlayIcsUnfinishedSound()\r
8738 {\r
8739   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8740 }\r
8741 \r
8742 VOID\r
8743 PlayAlarmSound()\r
8744 {\r
8745   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8746 }\r
8747 \r
8748 VOID\r
8749 PlayTellSound()\r
8750 {\r
8751   MyPlaySound(&textAttribs[ColorTell].sound);\r
8752 }\r
8753 \r
8754 \r
8755 VOID\r
8756 EchoOn()\r
8757 {\r
8758   HWND hInput;\r
8759   consoleEcho = TRUE;\r
8760   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8761   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8762   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8763 }\r
8764 \r
8765 \r
8766 VOID\r
8767 EchoOff()\r
8768 {\r
8769   CHARFORMAT cf;\r
8770   HWND hInput;\r
8771   consoleEcho = FALSE;\r
8772   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8773   /* This works OK: set text and background both to the same color */\r
8774   cf = consoleCF;\r
8775   cf.crTextColor = COLOR_ECHOOFF;\r
8776   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8777   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8778 }\r
8779 \r
8780 /* No Raw()...? */\r
8781 \r
8782 void Colorize(ColorClass cc, int continuation)\r
8783 {\r
8784   currentColorClass = cc;\r
8785   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8786   consoleCF.crTextColor = textAttribs[cc].color;\r
8787   consoleCF.dwEffects = textAttribs[cc].effects;\r
8788   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8789 }\r
8790 \r
8791 char *\r
8792 UserName()\r
8793 {\r
8794   static char buf[MSG_SIZ];\r
8795   DWORD bufsiz = MSG_SIZ;\r
8796 \r
8797   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8798         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8799   }\r
8800   if (!GetUserName(buf, &bufsiz)) {\r
8801     /*DisplayError("Error getting user name", GetLastError());*/\r
8802     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8803   }\r
8804   return buf;\r
8805 }\r
8806 \r
8807 char *\r
8808 HostName()\r
8809 {\r
8810   static char buf[MSG_SIZ];\r
8811   DWORD bufsiz = MSG_SIZ;\r
8812 \r
8813   if (!GetComputerName(buf, &bufsiz)) {\r
8814     /*DisplayError("Error getting host name", GetLastError());*/\r
8815     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8816   }\r
8817   return buf;\r
8818 }\r
8819 \r
8820 \r
8821 int\r
8822 ClockTimerRunning()\r
8823 {\r
8824   return clockTimerEvent != 0;\r
8825 }\r
8826 \r
8827 int\r
8828 StopClockTimer()\r
8829 {\r
8830   if (clockTimerEvent == 0) return FALSE;\r
8831   KillTimer(hwndMain, clockTimerEvent);\r
8832   clockTimerEvent = 0;\r
8833   return TRUE;\r
8834 }\r
8835 \r
8836 void\r
8837 StartClockTimer(long millisec)\r
8838 {\r
8839   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8840                              (UINT) millisec, NULL);\r
8841 }\r
8842 \r
8843 void\r
8844 DisplayWhiteClock(long timeRemaining, int highlight)\r
8845 {\r
8846   HDC hdc;\r
8847   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8848 \r
8849   if(appData.noGUI) return;\r
8850   hdc = GetDC(hwndMain);\r
8851   if (!IsIconic(hwndMain)) {\r
8852     DisplayAClock(hdc, timeRemaining, highlight, \r
8853                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8854   }\r
8855   if (highlight && iconCurrent == iconBlack) {\r
8856     iconCurrent = iconWhite;\r
8857     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8858     if (IsIconic(hwndMain)) {\r
8859       DrawIcon(hdc, 2, 2, iconCurrent);\r
8860     }\r
8861   }\r
8862   (void) ReleaseDC(hwndMain, hdc);\r
8863   if (hwndConsole)\r
8864     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8865 }\r
8866 \r
8867 void\r
8868 DisplayBlackClock(long timeRemaining, int highlight)\r
8869 {\r
8870   HDC hdc;\r
8871   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8872 \r
8873   if(appData.noGUI) return;\r
8874   hdc = GetDC(hwndMain);\r
8875   if (!IsIconic(hwndMain)) {\r
8876     DisplayAClock(hdc, timeRemaining, highlight, \r
8877                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8878   }\r
8879   if (highlight && iconCurrent == iconWhite) {\r
8880     iconCurrent = iconBlack;\r
8881     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8882     if (IsIconic(hwndMain)) {\r
8883       DrawIcon(hdc, 2, 2, iconCurrent);\r
8884     }\r
8885   }\r
8886   (void) ReleaseDC(hwndMain, hdc);\r
8887   if (hwndConsole)\r
8888     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8889 }\r
8890 \r
8891 \r
8892 int\r
8893 LoadGameTimerRunning()\r
8894 {\r
8895   return loadGameTimerEvent != 0;\r
8896 }\r
8897 \r
8898 int\r
8899 StopLoadGameTimer()\r
8900 {\r
8901   if (loadGameTimerEvent == 0) return FALSE;\r
8902   KillTimer(hwndMain, loadGameTimerEvent);\r
8903   loadGameTimerEvent = 0;\r
8904   return TRUE;\r
8905 }\r
8906 \r
8907 void\r
8908 StartLoadGameTimer(long millisec)\r
8909 {\r
8910   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8911                                 (UINT) millisec, NULL);\r
8912 }\r
8913 \r
8914 void\r
8915 AutoSaveGame()\r
8916 {\r
8917   char *defName;\r
8918   FILE *f;\r
8919   char fileTitle[MSG_SIZ];\r
8920 \r
8921   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8922   f = OpenFileDialog(hwndMain, "a", defName,\r
8923                      appData.oldSaveStyle ? "gam" : "pgn",\r
8924                      GAME_FILT, \r
8925                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8926   if (f != NULL) {\r
8927     SaveGame(f, 0, "");\r
8928     fclose(f);\r
8929   }\r
8930 }\r
8931 \r
8932 \r
8933 void\r
8934 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8935 {\r
8936   if (delayedTimerEvent != 0) {\r
8937     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8938       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8939     }\r
8940     KillTimer(hwndMain, delayedTimerEvent);\r
8941     delayedTimerEvent = 0;\r
8942     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8943     delayedTimerCallback();\r
8944   }\r
8945   delayedTimerCallback = cb;\r
8946   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8947                                 (UINT) millisec, NULL);\r
8948 }\r
8949 \r
8950 DelayedEventCallback\r
8951 GetDelayedEvent()\r
8952 {\r
8953   if (delayedTimerEvent) {\r
8954     return delayedTimerCallback;\r
8955   } else {\r
8956     return NULL;\r
8957   }\r
8958 }\r
8959 \r
8960 void\r
8961 CancelDelayedEvent()\r
8962 {\r
8963   if (delayedTimerEvent) {\r
8964     KillTimer(hwndMain, delayedTimerEvent);\r
8965     delayedTimerEvent = 0;\r
8966   }\r
8967 }\r
8968 \r
8969 DWORD GetWin32Priority(int nice)\r
8970 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8971 /*\r
8972 REALTIME_PRIORITY_CLASS     0x00000100\r
8973 HIGH_PRIORITY_CLASS         0x00000080\r
8974 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8975 NORMAL_PRIORITY_CLASS       0x00000020\r
8976 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8977 IDLE_PRIORITY_CLASS         0x00000040\r
8978 */\r
8979         if (nice < -15) return 0x00000080;\r
8980         if (nice < 0)   return 0x00008000;\r
8981         if (nice == 0)  return 0x00000020;\r
8982         if (nice < 15)  return 0x00004000;\r
8983         return 0x00000040;\r
8984 }\r
8985 \r
8986 void RunCommand(char *cmdLine)\r
8987 {\r
8988   /* Now create the child process. */\r
8989   STARTUPINFO siStartInfo;\r
8990   PROCESS_INFORMATION piProcInfo;\r
8991 \r
8992   siStartInfo.cb = sizeof(STARTUPINFO);\r
8993   siStartInfo.lpReserved = NULL;\r
8994   siStartInfo.lpDesktop = NULL;\r
8995   siStartInfo.lpTitle = NULL;\r
8996   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8997   siStartInfo.cbReserved2 = 0;\r
8998   siStartInfo.lpReserved2 = NULL;\r
8999   siStartInfo.hStdInput = NULL;\r
9000   siStartInfo.hStdOutput = NULL;\r
9001   siStartInfo.hStdError = NULL;\r
9002 \r
9003   CreateProcess(NULL,\r
9004                 cmdLine,           /* command line */\r
9005                 NULL,      /* process security attributes */\r
9006                 NULL,      /* primary thread security attrs */\r
9007                 TRUE,      /* handles are inherited */\r
9008                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9009                 NULL,      /* use parent's environment */\r
9010                 NULL,\r
9011                 &siStartInfo, /* STARTUPINFO pointer */\r
9012                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9013 \r
9014   CloseHandle(piProcInfo.hThread);\r
9015 }\r
9016 \r
9017 /* Start a child process running the given program.\r
9018    The process's standard output can be read from "from", and its\r
9019    standard input can be written to "to".\r
9020    Exit with fatal error if anything goes wrong.\r
9021    Returns an opaque pointer that can be used to destroy the process\r
9022    later.\r
9023 */\r
9024 int\r
9025 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9026 {\r
9027 #define BUFSIZE 4096\r
9028 \r
9029   HANDLE hChildStdinRd, hChildStdinWr,\r
9030     hChildStdoutRd, hChildStdoutWr;\r
9031   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9032   SECURITY_ATTRIBUTES saAttr;\r
9033   BOOL fSuccess;\r
9034   PROCESS_INFORMATION piProcInfo;\r
9035   STARTUPINFO siStartInfo;\r
9036   ChildProc *cp;\r
9037   char buf[MSG_SIZ];\r
9038   DWORD err;\r
9039 \r
9040   if (appData.debugMode) {\r
9041     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9042   }\r
9043 \r
9044   *pr = NoProc;\r
9045 \r
9046   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9047   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9048   saAttr.bInheritHandle = TRUE;\r
9049   saAttr.lpSecurityDescriptor = NULL;\r
9050 \r
9051   /*\r
9052    * The steps for redirecting child's STDOUT:\r
9053    *     1. Create anonymous pipe to be STDOUT for child.\r
9054    *     2. Create a noninheritable duplicate of read handle,\r
9055    *         and close the inheritable read handle.\r
9056    */\r
9057 \r
9058   /* Create a pipe for the child's STDOUT. */\r
9059   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9060     return GetLastError();\r
9061   }\r
9062 \r
9063   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9064   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9065                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9066                              FALSE,     /* not inherited */\r
9067                              DUPLICATE_SAME_ACCESS);\r
9068   if (! fSuccess) {\r
9069     return GetLastError();\r
9070   }\r
9071   CloseHandle(hChildStdoutRd);\r
9072 \r
9073   /*\r
9074    * The steps for redirecting child's STDIN:\r
9075    *     1. Create anonymous pipe to be STDIN for child.\r
9076    *     2. Create a noninheritable duplicate of write handle,\r
9077    *         and close the inheritable write handle.\r
9078    */\r
9079 \r
9080   /* Create a pipe for the child's STDIN. */\r
9081   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9082     return GetLastError();\r
9083   }\r
9084 \r
9085   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9086   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9087                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9088                              FALSE,     /* not inherited */\r
9089                              DUPLICATE_SAME_ACCESS);\r
9090   if (! fSuccess) {\r
9091     return GetLastError();\r
9092   }\r
9093   CloseHandle(hChildStdinWr);\r
9094 \r
9095   /* Arrange to (1) look in dir for the child .exe file, and\r
9096    * (2) have dir be the child's working directory.  Interpret\r
9097    * dir relative to the directory WinBoard loaded from. */\r
9098   GetCurrentDirectory(MSG_SIZ, buf);\r
9099   SetCurrentDirectory(installDir);\r
9100   SetCurrentDirectory(dir);\r
9101 \r
9102   /* Now create the child process. */\r
9103 \r
9104   siStartInfo.cb = sizeof(STARTUPINFO);\r
9105   siStartInfo.lpReserved = NULL;\r
9106   siStartInfo.lpDesktop = NULL;\r
9107   siStartInfo.lpTitle = NULL;\r
9108   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9109   siStartInfo.cbReserved2 = 0;\r
9110   siStartInfo.lpReserved2 = NULL;\r
9111   siStartInfo.hStdInput = hChildStdinRd;\r
9112   siStartInfo.hStdOutput = hChildStdoutWr;\r
9113   siStartInfo.hStdError = hChildStdoutWr;\r
9114 \r
9115   fSuccess = CreateProcess(NULL,\r
9116                            cmdLine,        /* command line */\r
9117                            NULL,           /* process security attributes */\r
9118                            NULL,           /* primary thread security attrs */\r
9119                            TRUE,           /* handles are inherited */\r
9120                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9121                            NULL,           /* use parent's environment */\r
9122                            NULL,\r
9123                            &siStartInfo, /* STARTUPINFO pointer */\r
9124                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9125 \r
9126   err = GetLastError();\r
9127   SetCurrentDirectory(buf); /* return to prev directory */\r
9128   if (! fSuccess) {\r
9129     return err;\r
9130   }\r
9131 \r
9132   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9133     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9134     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9135   }\r
9136 \r
9137   /* Close the handles we don't need in the parent */\r
9138   CloseHandle(piProcInfo.hThread);\r
9139   CloseHandle(hChildStdinRd);\r
9140   CloseHandle(hChildStdoutWr);\r
9141 \r
9142   /* Prepare return value */\r
9143   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9144   cp->kind = CPReal;\r
9145   cp->hProcess = piProcInfo.hProcess;\r
9146   cp->pid = piProcInfo.dwProcessId;\r
9147   cp->hFrom = hChildStdoutRdDup;\r
9148   cp->hTo = hChildStdinWrDup;\r
9149 \r
9150   *pr = (void *) cp;\r
9151 \r
9152   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9153      2000 where engines sometimes don't see the initial command(s)\r
9154      from WinBoard and hang.  I don't understand how that can happen,\r
9155      but the Sleep is harmless, so I've put it in.  Others have also\r
9156      reported what may be the same problem, so hopefully this will fix\r
9157      it for them too.  */\r
9158   Sleep(500);\r
9159 \r
9160   return NO_ERROR;\r
9161 }\r
9162 \r
9163 \r
9164 void\r
9165 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9166 {\r
9167   ChildProc *cp; int result;\r
9168 \r
9169   cp = (ChildProc *) pr;\r
9170   if (cp == NULL) return;\r
9171 \r
9172   switch (cp->kind) {\r
9173   case CPReal:\r
9174     /* TerminateProcess is considered harmful, so... */\r
9175     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9176     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9177     /* The following doesn't work because the chess program\r
9178        doesn't "have the same console" as WinBoard.  Maybe\r
9179        we could arrange for this even though neither WinBoard\r
9180        nor the chess program uses a console for stdio? */\r
9181     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9182 \r
9183     /* [AS] Special termination modes for misbehaving programs... */\r
9184     if( signal == 9 ) { \r
9185         result = TerminateProcess( cp->hProcess, 0 );\r
9186 \r
9187         if ( appData.debugMode) {\r
9188             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9189         }\r
9190     }\r
9191     else if( signal == 10 ) {\r
9192         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9193 \r
9194         if( dw != WAIT_OBJECT_0 ) {\r
9195             result = TerminateProcess( cp->hProcess, 0 );\r
9196 \r
9197             if ( appData.debugMode) {\r
9198                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9199             }\r
9200 \r
9201         }\r
9202     }\r
9203 \r
9204     CloseHandle(cp->hProcess);\r
9205     break;\r
9206 \r
9207   case CPComm:\r
9208     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9209     break;\r
9210 \r
9211   case CPSock:\r
9212     closesocket(cp->sock);\r
9213     WSACleanup();\r
9214     break;\r
9215 \r
9216   case CPRcmd:\r
9217     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9218     closesocket(cp->sock);\r
9219     closesocket(cp->sock2);\r
9220     WSACleanup();\r
9221     break;\r
9222   }\r
9223   free(cp);\r
9224 }\r
9225 \r
9226 void\r
9227 InterruptChildProcess(ProcRef pr)\r
9228 {\r
9229   ChildProc *cp;\r
9230 \r
9231   cp = (ChildProc *) pr;\r
9232   if (cp == NULL) return;\r
9233   switch (cp->kind) {\r
9234   case CPReal:\r
9235     /* The following doesn't work because the chess program\r
9236        doesn't "have the same console" as WinBoard.  Maybe\r
9237        we could arrange for this even though neither WinBoard\r
9238        nor the chess program uses a console for stdio */\r
9239     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9240     break;\r
9241 \r
9242   case CPComm:\r
9243   case CPSock:\r
9244     /* Can't interrupt */\r
9245     break;\r
9246 \r
9247   case CPRcmd:\r
9248     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9249     break;\r
9250   }\r
9251 }\r
9252 \r
9253 \r
9254 int\r
9255 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9256 {\r
9257   char cmdLine[MSG_SIZ];\r
9258 \r
9259   if (port[0] == NULLCHAR) {\r
9260     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9261   } else {\r
9262     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9263   }\r
9264   return StartChildProcess(cmdLine, "", pr);\r
9265 }\r
9266 \r
9267 \r
9268 /* Code to open TCP sockets */\r
9269 \r
9270 int\r
9271 OpenTCP(char *host, char *port, ProcRef *pr)\r
9272 {\r
9273   ChildProc *cp;\r
9274   int err;\r
9275   SOCKET s;\r
9276 \r
9277   struct sockaddr_in sa, mysa;\r
9278   struct hostent FAR *hp;\r
9279   unsigned short uport;\r
9280   WORD wVersionRequested;\r
9281   WSADATA wsaData;\r
9282 \r
9283   /* Initialize socket DLL */\r
9284   wVersionRequested = MAKEWORD(1, 1);\r
9285   err = WSAStartup(wVersionRequested, &wsaData);\r
9286   if (err != 0) return err;\r
9287 \r
9288   /* Make socket */\r
9289   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9290     err = WSAGetLastError();\r
9291     WSACleanup();\r
9292     return err;\r
9293   }\r
9294 \r
9295   /* Bind local address using (mostly) don't-care values.\r
9296    */\r
9297   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9298   mysa.sin_family = AF_INET;\r
9299   mysa.sin_addr.s_addr = INADDR_ANY;\r
9300   uport = (unsigned short) 0;\r
9301   mysa.sin_port = htons(uport);\r
9302   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9303       == SOCKET_ERROR) {\r
9304     err = WSAGetLastError();\r
9305     WSACleanup();\r
9306     return err;\r
9307   }\r
9308 \r
9309   /* Resolve remote host name */\r
9310   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9311   if (!(hp = gethostbyname(host))) {\r
9312     unsigned int b0, b1, b2, b3;\r
9313 \r
9314     err = WSAGetLastError();\r
9315 \r
9316     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9317       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9318       hp->h_addrtype = AF_INET;\r
9319       hp->h_length = 4;\r
9320       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9321       hp->h_addr_list[0] = (char *) malloc(4);\r
9322       hp->h_addr_list[0][0] = (char) b0;\r
9323       hp->h_addr_list[0][1] = (char) b1;\r
9324       hp->h_addr_list[0][2] = (char) b2;\r
9325       hp->h_addr_list[0][3] = (char) b3;\r
9326     } else {\r
9327       WSACleanup();\r
9328       return err;\r
9329     }\r
9330   }\r
9331   sa.sin_family = hp->h_addrtype;\r
9332   uport = (unsigned short) atoi(port);\r
9333   sa.sin_port = htons(uport);\r
9334   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9335 \r
9336   /* Make connection */\r
9337   if (connect(s, (struct sockaddr *) &sa,\r
9338               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9339     err = WSAGetLastError();\r
9340     WSACleanup();\r
9341     return err;\r
9342   }\r
9343 \r
9344   /* Prepare return value */\r
9345   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9346   cp->kind = CPSock;\r
9347   cp->sock = s;\r
9348   *pr = (ProcRef *) cp;\r
9349 \r
9350   return NO_ERROR;\r
9351 }\r
9352 \r
9353 int\r
9354 OpenCommPort(char *name, ProcRef *pr)\r
9355 {\r
9356   HANDLE h;\r
9357   COMMTIMEOUTS ct;\r
9358   ChildProc *cp;\r
9359   char fullname[MSG_SIZ];\r
9360 \r
9361   if (*name != '\\')\r
9362     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9363   else\r
9364     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9365 \r
9366   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9367                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9368   if (h == (HANDLE) -1) {\r
9369     return GetLastError();\r
9370   }\r
9371   hCommPort = h;\r
9372 \r
9373   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9374 \r
9375   /* Accumulate characters until a 100ms pause, then parse */\r
9376   ct.ReadIntervalTimeout = 100;\r
9377   ct.ReadTotalTimeoutMultiplier = 0;\r
9378   ct.ReadTotalTimeoutConstant = 0;\r
9379   ct.WriteTotalTimeoutMultiplier = 0;\r
9380   ct.WriteTotalTimeoutConstant = 0;\r
9381   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9382 \r
9383   /* Prepare return value */\r
9384   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9385   cp->kind = CPComm;\r
9386   cp->hFrom = h;\r
9387   cp->hTo = h;\r
9388   *pr = (ProcRef *) cp;\r
9389 \r
9390   return NO_ERROR;\r
9391 }\r
9392 \r
9393 int\r
9394 OpenLoopback(ProcRef *pr)\r
9395 {\r
9396   DisplayFatalError(_("Not implemented"), 0, 1);\r
9397   return NO_ERROR;\r
9398 }\r
9399 \r
9400 \r
9401 int\r
9402 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9403 {\r
9404   ChildProc *cp;\r
9405   int err;\r
9406   SOCKET s, s2, s3;\r
9407   struct sockaddr_in sa, mysa;\r
9408   struct hostent FAR *hp;\r
9409   unsigned short uport;\r
9410   WORD wVersionRequested;\r
9411   WSADATA wsaData;\r
9412   int fromPort;\r
9413   char stderrPortStr[MSG_SIZ];\r
9414 \r
9415   /* Initialize socket DLL */\r
9416   wVersionRequested = MAKEWORD(1, 1);\r
9417   err = WSAStartup(wVersionRequested, &wsaData);\r
9418   if (err != 0) return err;\r
9419 \r
9420   /* Resolve remote host name */\r
9421   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9422   if (!(hp = gethostbyname(host))) {\r
9423     unsigned int b0, b1, b2, b3;\r
9424 \r
9425     err = WSAGetLastError();\r
9426 \r
9427     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9428       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9429       hp->h_addrtype = AF_INET;\r
9430       hp->h_length = 4;\r
9431       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9432       hp->h_addr_list[0] = (char *) malloc(4);\r
9433       hp->h_addr_list[0][0] = (char) b0;\r
9434       hp->h_addr_list[0][1] = (char) b1;\r
9435       hp->h_addr_list[0][2] = (char) b2;\r
9436       hp->h_addr_list[0][3] = (char) b3;\r
9437     } else {\r
9438       WSACleanup();\r
9439       return err;\r
9440     }\r
9441   }\r
9442   sa.sin_family = hp->h_addrtype;\r
9443   uport = (unsigned short) 514;\r
9444   sa.sin_port = htons(uport);\r
9445   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9446 \r
9447   /* Bind local socket to unused "privileged" port address\r
9448    */\r
9449   s = INVALID_SOCKET;\r
9450   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9451   mysa.sin_family = AF_INET;\r
9452   mysa.sin_addr.s_addr = INADDR_ANY;\r
9453   for (fromPort = 1023;; fromPort--) {\r
9454     if (fromPort < 0) {\r
9455       WSACleanup();\r
9456       return WSAEADDRINUSE;\r
9457     }\r
9458     if (s == INVALID_SOCKET) {\r
9459       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9460         err = WSAGetLastError();\r
9461         WSACleanup();\r
9462         return err;\r
9463       }\r
9464     }\r
9465     uport = (unsigned short) fromPort;\r
9466     mysa.sin_port = htons(uport);\r
9467     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9468         == SOCKET_ERROR) {\r
9469       err = WSAGetLastError();\r
9470       if (err == WSAEADDRINUSE) continue;\r
9471       WSACleanup();\r
9472       return err;\r
9473     }\r
9474     if (connect(s, (struct sockaddr *) &sa,\r
9475       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9476       err = WSAGetLastError();\r
9477       if (err == WSAEADDRINUSE) {\r
9478         closesocket(s);\r
9479         s = -1;\r
9480         continue;\r
9481       }\r
9482       WSACleanup();\r
9483       return err;\r
9484     }\r
9485     break;\r
9486   }\r
9487 \r
9488   /* Bind stderr local socket to unused "privileged" port address\r
9489    */\r
9490   s2 = INVALID_SOCKET;\r
9491   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9492   mysa.sin_family = AF_INET;\r
9493   mysa.sin_addr.s_addr = INADDR_ANY;\r
9494   for (fromPort = 1023;; fromPort--) {\r
9495     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9496     if (fromPort < 0) {\r
9497       (void) closesocket(s);\r
9498       WSACleanup();\r
9499       return WSAEADDRINUSE;\r
9500     }\r
9501     if (s2 == INVALID_SOCKET) {\r
9502       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9503         err = WSAGetLastError();\r
9504         closesocket(s);\r
9505         WSACleanup();\r
9506         return err;\r
9507       }\r
9508     }\r
9509     uport = (unsigned short) fromPort;\r
9510     mysa.sin_port = htons(uport);\r
9511     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9512         == SOCKET_ERROR) {\r
9513       err = WSAGetLastError();\r
9514       if (err == WSAEADDRINUSE) continue;\r
9515       (void) closesocket(s);\r
9516       WSACleanup();\r
9517       return err;\r
9518     }\r
9519     if (listen(s2, 1) == SOCKET_ERROR) {\r
9520       err = WSAGetLastError();\r
9521       if (err == WSAEADDRINUSE) {\r
9522         closesocket(s2);\r
9523         s2 = INVALID_SOCKET;\r
9524         continue;\r
9525       }\r
9526       (void) closesocket(s);\r
9527       (void) closesocket(s2);\r
9528       WSACleanup();\r
9529       return err;\r
9530     }\r
9531     break;\r
9532   }\r
9533   prevStderrPort = fromPort; // remember port used\r
9534   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9535 \r
9536   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9537     err = WSAGetLastError();\r
9538     (void) closesocket(s);\r
9539     (void) closesocket(s2);\r
9540     WSACleanup();\r
9541     return err;\r
9542   }\r
9543 \r
9544   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9545     err = WSAGetLastError();\r
9546     (void) closesocket(s);\r
9547     (void) closesocket(s2);\r
9548     WSACleanup();\r
9549     return err;\r
9550   }\r
9551   if (*user == NULLCHAR) user = UserName();\r
9552   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9553     err = WSAGetLastError();\r
9554     (void) closesocket(s);\r
9555     (void) closesocket(s2);\r
9556     WSACleanup();\r
9557     return err;\r
9558   }\r
9559   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9560     err = WSAGetLastError();\r
9561     (void) closesocket(s);\r
9562     (void) closesocket(s2);\r
9563     WSACleanup();\r
9564     return err;\r
9565   }\r
9566 \r
9567   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9568     err = WSAGetLastError();\r
9569     (void) closesocket(s);\r
9570     (void) closesocket(s2);\r
9571     WSACleanup();\r
9572     return err;\r
9573   }\r
9574   (void) closesocket(s2);  /* Stop listening */\r
9575 \r
9576   /* Prepare return value */\r
9577   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9578   cp->kind = CPRcmd;\r
9579   cp->sock = s;\r
9580   cp->sock2 = s3;\r
9581   *pr = (ProcRef *) cp;\r
9582 \r
9583   return NO_ERROR;\r
9584 }\r
9585 \r
9586 \r
9587 InputSourceRef\r
9588 AddInputSource(ProcRef pr, int lineByLine,\r
9589                InputCallback func, VOIDSTAR closure)\r
9590 {\r
9591   InputSource *is, *is2 = NULL;\r
9592   ChildProc *cp = (ChildProc *) pr;\r
9593 \r
9594   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9595   is->lineByLine = lineByLine;\r
9596   is->func = func;\r
9597   is->closure = closure;\r
9598   is->second = NULL;\r
9599   is->next = is->buf;\r
9600   if (pr == NoProc) {\r
9601     is->kind = CPReal;\r
9602     consoleInputSource = is;\r
9603   } else {\r
9604     is->kind = cp->kind;\r
9605     /* \r
9606         [AS] Try to avoid a race condition if the thread is given control too early:\r
9607         we create all threads suspended so that the is->hThread variable can be\r
9608         safely assigned, then let the threads start with ResumeThread.\r
9609     */\r
9610     switch (cp->kind) {\r
9611     case CPReal:\r
9612       is->hFile = cp->hFrom;\r
9613       cp->hFrom = NULL; /* now owned by InputThread */\r
9614       is->hThread =\r
9615         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9616                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9617       break;\r
9618 \r
9619     case CPComm:\r
9620       is->hFile = cp->hFrom;\r
9621       cp->hFrom = NULL; /* now owned by InputThread */\r
9622       is->hThread =\r
9623         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9624                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9625       break;\r
9626 \r
9627     case CPSock:\r
9628       is->sock = cp->sock;\r
9629       is->hThread =\r
9630         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9631                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9632       break;\r
9633 \r
9634     case CPRcmd:\r
9635       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9636       *is2 = *is;\r
9637       is->sock = cp->sock;\r
9638       is->second = is2;\r
9639       is2->sock = cp->sock2;\r
9640       is2->second = is2;\r
9641       is->hThread =\r
9642         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9643                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9644       is2->hThread =\r
9645         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9646                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9647       break;\r
9648     }\r
9649 \r
9650     if( is->hThread != NULL ) {\r
9651         ResumeThread( is->hThread );\r
9652     }\r
9653 \r
9654     if( is2 != NULL && is2->hThread != NULL ) {\r
9655         ResumeThread( is2->hThread );\r
9656     }\r
9657   }\r
9658 \r
9659   return (InputSourceRef) is;\r
9660 }\r
9661 \r
9662 void\r
9663 RemoveInputSource(InputSourceRef isr)\r
9664 {\r
9665   InputSource *is;\r
9666 \r
9667   is = (InputSource *) isr;\r
9668   is->hThread = NULL;  /* tell thread to stop */\r
9669   CloseHandle(is->hThread);\r
9670   if (is->second != NULL) {\r
9671     is->second->hThread = NULL;\r
9672     CloseHandle(is->second->hThread);\r
9673   }\r
9674 }\r
9675 \r
9676 int no_wrap(char *message, int count)\r
9677 {\r
9678     ConsoleOutput(message, count, FALSE);\r
9679     return count;\r
9680 }\r
9681 \r
9682 int\r
9683 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9684 {\r
9685   DWORD dOutCount;\r
9686   int outCount = SOCKET_ERROR;\r
9687   ChildProc *cp = (ChildProc *) pr;\r
9688   static OVERLAPPED ovl;\r
9689   static int line = 0;\r
9690 \r
9691   if (pr == NoProc)\r
9692   {\r
9693     if (appData.noJoin || !appData.useInternalWrap)\r
9694       return no_wrap(message, count);\r
9695     else\r
9696     {\r
9697       int width = get_term_width();\r
9698       int len = wrap(NULL, message, count, width, &line);\r
9699       char *msg = malloc(len);\r
9700       int dbgchk;\r
9701 \r
9702       if (!msg)\r
9703         return no_wrap(message, count);\r
9704       else\r
9705       {\r
9706         dbgchk = wrap(msg, message, count, width, &line);\r
9707         if (dbgchk != len && appData.debugMode)\r
9708             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9709         ConsoleOutput(msg, len, FALSE);\r
9710         free(msg);\r
9711         return len;\r
9712       }\r
9713     }\r
9714   }\r
9715 \r
9716   if (ovl.hEvent == NULL) {\r
9717     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9718   }\r
9719   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9720 \r
9721   switch (cp->kind) {\r
9722   case CPSock:\r
9723   case CPRcmd:\r
9724     outCount = send(cp->sock, message, count, 0);\r
9725     if (outCount == SOCKET_ERROR) {\r
9726       *outError = WSAGetLastError();\r
9727     } else {\r
9728       *outError = NO_ERROR;\r
9729     }\r
9730     break;\r
9731 \r
9732   case CPReal:\r
9733     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9734                   &dOutCount, NULL)) {\r
9735       *outError = NO_ERROR;\r
9736       outCount = (int) dOutCount;\r
9737     } else {\r
9738       *outError = GetLastError();\r
9739     }\r
9740     break;\r
9741 \r
9742   case CPComm:\r
9743     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9744                             &dOutCount, &ovl);\r
9745     if (*outError == NO_ERROR) {\r
9746       outCount = (int) dOutCount;\r
9747     }\r
9748     break;\r
9749   }\r
9750   return outCount;\r
9751 }\r
9752 \r
9753 void\r
9754 DoSleep(int n)\r
9755 {\r
9756     if(n != 0) Sleep(n);\r
9757 }\r
9758 \r
9759 int\r
9760 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9761                        long msdelay)\r
9762 {\r
9763   /* Ignore delay, not implemented for WinBoard */\r
9764   return OutputToProcess(pr, message, count, outError);\r
9765 }\r
9766 \r
9767 \r
9768 void\r
9769 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9770                         char *buf, int count, int error)\r
9771 {\r
9772   DisplayFatalError(_("Not implemented"), 0, 1);\r
9773 }\r
9774 \r
9775 /* see wgamelist.c for Game List functions */\r
9776 /* see wedittags.c for Edit Tags functions */\r
9777 \r
9778 \r
9779 int\r
9780 ICSInitScript()\r
9781 {\r
9782   FILE *f;\r
9783   char buf[MSG_SIZ];\r
9784   char *dummy;\r
9785 \r
9786   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9787     f = fopen(buf, "r");\r
9788     if (f != NULL) {\r
9789       ProcessICSInitScript(f);\r
9790       fclose(f);\r
9791       return TRUE;\r
9792     }\r
9793   }\r
9794   return FALSE;\r
9795 }\r
9796 \r
9797 \r
9798 VOID\r
9799 StartAnalysisClock()\r
9800 {\r
9801   if (analysisTimerEvent) return;\r
9802   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9803                                         (UINT) 2000, NULL);\r
9804 }\r
9805 \r
9806 VOID\r
9807 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9808 {\r
9809   highlightInfo.sq[0].x = fromX;\r
9810   highlightInfo.sq[0].y = fromY;\r
9811   highlightInfo.sq[1].x = toX;\r
9812   highlightInfo.sq[1].y = toY;\r
9813 }\r
9814 \r
9815 VOID\r
9816 ClearHighlights()\r
9817 {\r
9818   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9819     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9820 }\r
9821 \r
9822 VOID\r
9823 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9824 {\r
9825   premoveHighlightInfo.sq[0].x = fromX;\r
9826   premoveHighlightInfo.sq[0].y = fromY;\r
9827   premoveHighlightInfo.sq[1].x = toX;\r
9828   premoveHighlightInfo.sq[1].y = toY;\r
9829 }\r
9830 \r
9831 VOID\r
9832 ClearPremoveHighlights()\r
9833 {\r
9834   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9835     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9836 }\r
9837 \r
9838 VOID\r
9839 ShutDownFrontEnd()\r
9840 {\r
9841   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9842   DeleteClipboardTempFiles();\r
9843 }\r
9844 \r
9845 void\r
9846 BoardToTop()\r
9847 {\r
9848     if (IsIconic(hwndMain))\r
9849       ShowWindow(hwndMain, SW_RESTORE);\r
9850 \r
9851     SetActiveWindow(hwndMain);\r
9852 }\r
9853 \r
9854 /*\r
9855  * Prototypes for animation support routines\r
9856  */\r
9857 static void ScreenSquare(int column, int row, POINT * pt);\r
9858 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9859      POINT frames[], int * nFrames);\r
9860 \r
9861 \r
9862 #define kFactor 4\r
9863 \r
9864 void\r
9865 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9866 {       // [HGM] atomic: animate blast wave\r
9867         int i;\r
9868 \r
9869         explodeInfo.fromX = fromX;\r
9870         explodeInfo.fromY = fromY;\r
9871         explodeInfo.toX = toX;\r
9872         explodeInfo.toY = toY;\r
9873         for(i=1; i<4*kFactor; i++) {\r
9874             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9875             DrawPosition(FALSE, board);\r
9876             Sleep(appData.animSpeed);\r
9877         }\r
9878         explodeInfo.radius = 0;\r
9879         DrawPosition(TRUE, board);\r
9880 }\r
9881 \r
9882 void\r
9883 AnimateMove(board, fromX, fromY, toX, toY)\r
9884      Board board;\r
9885      int fromX;\r
9886      int fromY;\r
9887      int toX;\r
9888      int toY;\r
9889 {\r
9890   ChessSquare piece;\r
9891   POINT start, finish, mid;\r
9892   POINT frames[kFactor * 2 + 1];\r
9893   int nFrames, n;\r
9894 \r
9895   if (!appData.animate) return;\r
9896   if (doingSizing) return;\r
9897   if (fromY < 0 || fromX < 0) return;\r
9898   piece = board[fromY][fromX];\r
9899   if (piece >= EmptySquare) return;\r
9900 \r
9901   ScreenSquare(fromX, fromY, &start);\r
9902   ScreenSquare(toX, toY, &finish);\r
9903 \r
9904   /* All moves except knight jumps move in straight line */\r
9905   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9906     mid.x = start.x + (finish.x - start.x) / 2;\r
9907     mid.y = start.y + (finish.y - start.y) / 2;\r
9908   } else {\r
9909     /* Knight: make straight movement then diagonal */\r
9910     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9911        mid.x = start.x + (finish.x - start.x) / 2;\r
9912        mid.y = start.y;\r
9913      } else {\r
9914        mid.x = start.x;\r
9915        mid.y = start.y + (finish.y - start.y) / 2;\r
9916      }\r
9917   }\r
9918   \r
9919   /* Don't use as many frames for very short moves */\r
9920   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9921     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9922   else\r
9923     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9924 \r
9925   animInfo.from.x = fromX;\r
9926   animInfo.from.y = fromY;\r
9927   animInfo.to.x = toX;\r
9928   animInfo.to.y = toY;\r
9929   animInfo.lastpos = start;\r
9930   animInfo.piece = piece;\r
9931   for (n = 0; n < nFrames; n++) {\r
9932     animInfo.pos = frames[n];\r
9933     DrawPosition(FALSE, NULL);\r
9934     animInfo.lastpos = animInfo.pos;\r
9935     Sleep(appData.animSpeed);\r
9936   }\r
9937   animInfo.pos = finish;\r
9938   DrawPosition(FALSE, NULL);\r
9939   animInfo.piece = EmptySquare;\r
9940   Explode(board, fromX, fromY, toX, toY);\r
9941 }\r
9942 \r
9943 /*      Convert board position to corner of screen rect and color       */\r
9944 \r
9945 static void\r
9946 ScreenSquare(column, row, pt)\r
9947      int column; int row; POINT * pt;\r
9948 {\r
9949   if (flipView) {\r
9950     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9951     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9952   } else {\r
9953     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9954     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9955   }\r
9956 }\r
9957 \r
9958 /*      Generate a series of frame coords from start->mid->finish.\r
9959         The movement rate doubles until the half way point is\r
9960         reached, then halves back down to the final destination,\r
9961         which gives a nice slow in/out effect. The algorithmn\r
9962         may seem to generate too many intermediates for short\r
9963         moves, but remember that the purpose is to attract the\r
9964         viewers attention to the piece about to be moved and\r
9965         then to where it ends up. Too few frames would be less\r
9966         noticeable.                                             */\r
9967 \r
9968 static void\r
9969 Tween(start, mid, finish, factor, frames, nFrames)\r
9970      POINT * start; POINT * mid;\r
9971      POINT * finish; int factor;\r
9972      POINT frames[]; int * nFrames;\r
9973 {\r
9974   int n, fraction = 1, count = 0;\r
9975 \r
9976   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9977   for (n = 0; n < factor; n++)\r
9978     fraction *= 2;\r
9979   for (n = 0; n < factor; n++) {\r
9980     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9981     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9982     count ++;\r
9983     fraction = fraction / 2;\r
9984   }\r
9985   \r
9986   /* Midpoint */\r
9987   frames[count] = *mid;\r
9988   count ++;\r
9989   \r
9990   /* Slow out, stepping 1/2, then 1/4, ... */\r
9991   fraction = 2;\r
9992   for (n = 0; n < factor; n++) {\r
9993     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9994     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9995     count ++;\r
9996     fraction = fraction * 2;\r
9997   }\r
9998   *nFrames = count;\r
9999 }\r
10000 \r
10001 void\r
10002 SettingsPopUp(ChessProgramState *cps)\r
10003 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10004       EngineOptionsPopup(savedHwnd, cps);\r
10005 }\r
10006 \r
10007 int flock(int fid, int code)\r
10008 {\r
10009     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10010     OVERLAPPED ov;\r
10011     ov.hEvent = NULL;\r
10012     ov.Offset = 0;\r
10013     ov.OffsetHigh = 0;\r
10014     switch(code) {\r
10015       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10016       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10017       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10018       default: return -1;\r
10019     }\r
10020     return 0;\r
10021 }\r
10022 \r
10023 char *\r
10024 Col2Text (int n)\r
10025 {\r
10026     static int i=0;\r
10027     static char col[8][20];\r
10028     COLORREF color = *(COLORREF *) colorVariable[n];\r
10029     i = i+1 & 7;\r
10030     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10031     return col[i];\r
10032 }\r
10033 \r
10034 void\r
10035 ActivateTheme (int new)\r
10036 {   // Redo initialization of features depending on options that can occur in themes\r
10037    InitTextures();\r
10038    if(new) InitDrawingColors();\r
10039    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10040    InitDrawingSizes(boardSize, 0);\r
10041    InvalidateRect(hwndMain, NULL, TRUE);\r
10042 }\r